<commit_before>#include "rendersystem.hpp"
#include "componentmanager.hpp"
#include "rendercomponent.hpp"
#include "movecomponent.hpp"
#include "sizecomponent.hpp"
#include <iostream>
#include "spatialindexer.hpp"
#include "heap.hpp"
#include "dynamicarray.hpp"
#include "heapsort.hpp"
#include <queue>
#include "namecomponent.hpp"
#include "systemmanager.hpp"
#include "hashgridsystem.hpp"
#include "deltatime.hpp"
#include "camerasystem.hpp"

using namespace std;

bool RenderData::operator< (const RenderData& rhs) const {
    return
        renderComponent->zindex_base == rhs.renderComponent->zindex_base ?
        renderComponent->zindex < rhs.renderComponent->zindex :
        renderComponent->zindex_base < rhs.renderComponent->zindex_base;
}

bool RenderData::operator> (const RenderData& rhs) const { return rhs < *this; }
bool RenderData::operator<=(const RenderData& rhs) const { return !(*this > rhs); }
bool RenderData::operator>=(const RenderData& rhs) const { return !(*this < rhs); }

RenderSystem::RenderSystem() {
	//Initialize SDL
    if(SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
    {
        cout << "ERROR: SDL could not initialize! SDL_Error: " << SDL_GetError() << endl;
    } else {
        window = SDL_CreateWindow(
            "SDL Tutorial",
            SDL_WINDOWPOS_UNDEFINED,
            SDL_WINDOWPOS_UNDEFINED,
            SCREEN_WIDTH,
            SCREEN_HEIGHT,
            SDL_WINDOW_SHOWN);

        if(!window) {
            cout << "ERROR: Window could not be created! SDL_Error: " << SDL_GetError() << endl;
        } else {
            renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
            if(!renderer) {
                cout << "ERROR: Renderer could not be created! SDL_Error: " << SDL_GetError() << endl;
            } else {

				//imgFlags is used to initialize PNG-loading
				//so that IMG_Load can be used instead of SDL_LoadBMP
				int imgFlags = IMG_INIT_PNG;
				if(!(IMG_Init(imgFlags) & imgFlags)) {
					cout << "ERROR: Couldnt initialze PNG usage!" << endl;
				} else {

		            SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF);

                    fontTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, SCREEN_WIDTH, SCREEN_HEIGHT);
                    if(!fontTexture) {
						cout << "ERROR: Couldn't create fontTexture!" << endl;
						cout << "Screen will probably go black from now on" << endl;
						cout << "SDL_GetError(): " << SDL_GetError() << endl;
					}
                    SDL_SetTextureBlendMode(fontTexture, SDL_BLENDMODE_BLEND);

			        worldTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 8192, 8192);
					if(!worldTexture) {
						cout << "ERROR: Couldn't create worldTexture!" << endl;
						cout << "Screen will probably go black from now on" << endl;
						cout << "SDL_GetError(): " << SDL_GetError() << endl;
					}
					SDL_SetRenderTarget(renderer, worldTexture);
					SDL_RenderClear(renderer);
				}

                if(TTF_Init() < 0 ) {
                    cout << "ERROR: Couldn't initialize SDL2_ttf!" << endl;
                } else {
                    font = TTF_OpenFont("/usr/share/fonts/TTF/DejaVuSansMono.ttf", 11);
                    if(!font) {
                        cout << "ERROR: Failed to load font!" << endl;
                    }
                }
            }
        }
    }

    //We want to access surfaces by their image path later on, so store paths right away
    for(auto path :{
            "./resources/images/HelloWorld.bmp",
            "./resources/images/player.bmp",
            "./resources/images/grass.png",
			"./resources/images/SmallTree.png",
            "./resources/images/playerv2.png",

            "./resources/images/playerv3_front.png",
            "./resources/images/playerv3_back.png",
            "./resources/images/playerv3_left.png",
            "./resources/images/playerv3_right.png",

            "./resources/images/playerv3_front_run1.png",
            "./resources/images/playerv3_front_run2.png",
            "./resources/images/playerv3_front_run3.png",
            "./resources/images/playerv3_front_run4.png",

            "./resources/images/bloodsplatter1_1.png",
            "./resources/images/bloodsplatter1_2.png",
            "./resources/images/bloodsplatter1_3.png",
            "./resources/images/bloodsplatter1_4.png",
            "./resources/images/bloodsplatter1_5.png",
            "./resources/images/bloodsplatter1_6.png",
            "./resources/images/bloodsplatter1_7.png",
            "./resources/images/bloodsplatter1_8.png",
            "./resources/images/bloodsplatter1_9.png",
            "./resources/images/bloodsplatter1_10.png",
            "./resources/images/bloodsplatter1_11.png",

            "./resources/images/yellow_flower.png",
            "./resources/images/green_flower.png",
            "./resources/images/violet_flower.png",
            "./resources/images/blue_flower.png",
            }
        )
    {
        SDL_Surface* rawImage = IMG_Load(path);
        if(!rawImage) {
            cout << "ERROR: Couldn't load image on: " << path << endl;
        } else {
            //Make purple parts of images transparent by using color keying
            SDL_SetColorKey(rawImage, SDL_TRUE, SDL_MapRGB( rawImage->format, 0xFF, 0x00, 0xFF ));
            SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, rawImage);
            if(!texture) {
                cout << "ERROR: Couldn't convert surface on addr " << rawImage << " to texture! SDL_Error: " << SDL_GetError() << endl;
            } else {
                SDL_FreeSurface(rawImage);
                int w, h;
                SDL_QueryTexture(texture, NULL, NULL, &w, &h);
                textureDatas.insert(
                    make_pair(
                        path,
                        TextureData{
                            texture,
                            (unsigned int)w,
                            (unsigned int)h
                        }
                    )
                );
            }
        }
    }
}

RenderSystem::~RenderSystem() {
    TTF_CloseFont(font);
    TTF_Quit();

    //Free all loaded images
    for(auto textureData : textureDatas) {
        SDL_DestroyTexture(get<1>(textureData).texture);
    }

    SDL_DestroyTexture(fontTexture);
    SDL_DestroyTexture(worldTexture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_QuitSubSystem(SDL_INIT_VIDEO);
}

void RenderSystem::add(ID id) {
	ids.insert(id);
	activeIds.push(id);
}

void RenderSystem::remove(ID id) {
	ids.erase(id);
}

void RenderSystem::update() {
	heap<RenderData> pq; //TODO: Use reference instead (no need for copying!)
	auto spatialIndexer = dynamic_cast<SpatialIndexer*>(systemManager->getSystem("TextureHashGridSystem"));

    //Will contain all rectangles where a redraw is required
    queue<SpatialIndexer::Rect> drawQueue;

    //All drawareas saved from previous frame should be drawn this frame
	drawQueue.swap(previousDrawAreas);

	//For all activeIds in rendersystem...
	while(!activeIds.empty()) {
		//Draw everything within the activeIds texturearea
        auto drawArea = spatialIndexer->getBoundingBox(activeIds.front());
		drawQueue.push(drawArea); activeIds.pop();

        //The texturearea should be saved for next frame, it must be redrawn then too
		renderArea(drawArea);
	}

    while(!drawQueue.empty()) {
        const auto& drawArea = drawQueue.front();

        //Make sure all overlapping ids have their part in the intersection area
    	//are redrawn
    	auto spatialIndexer = dynamic_cast<SpatialIndexer*>(systemManager->getSystem("TextureHashGridSystem"));

    	for(auto id : spatialIndexer->query(drawArea)) {
    		auto& mc = componentManager->moveComponents.at(id);
    		auto& rc = componentManager->renderComponents.at(id);

    		//Get the intersection between an entity within drawarea and the drawarea
    		const auto intersectionArea = spatialIndexer->getIntersectionArea(drawArea, spatialIndexer->getBoundingBox(id));

    		//Only draw that portion of the texture that intersects
    		calculateZIndex(id);
    		pq.insert({id, &rc, rc.textureData, {
    				(int)(intersectionArea.x - mc.pos.x - rc.xoffset),
    				(int)(intersectionArea.y - mc.pos.y - rc.yoffset),
    				(int)intersectionArea.w,
    				(int)intersectionArea.h }, {
    				(int)intersectionArea.x,
    				(int)intersectionArea.y,
    				(int)intersectionArea.w,
    				(int)intersectionArea.h}

    				}
    			);
    	}

        //Dont move this piece of code. A reference to this element is used above!
        drawQueue.pop();
    }

    //Draw to worldTexture
	SDL_SetRenderTarget(renderer, worldTexture);

	while(!pq.isEmpty()) {
		render(pq.poll());
	}

    //Put the fps on tmp (text->surface->tmp->fontTexture->default render target)
    const std::string str = std::to_string((int)(1/deltaTime->delta())) + "fps";
    printText(Text(str, 0, 0, {231, 195, 175}));

    //Render all texts
    renderTexts();

    //Draw to default render target (NULL)
    SDL_SetRenderTarget(renderer, NULL);
    SDL_RenderClear(renderer);
    SDL_RenderCopy(renderer, worldTexture, &cameraSystem->getCamera(), NULL);
    SDL_RenderCopy(renderer, fontTexture, nullptr, nullptr);
	SDL_RenderPresent(renderer);
}

unsigned int RenderSystem::count() const {
	return ids.size();
}

void RenderSystem::render(const RenderData& rd) const {
    SDL_RenderCopy(renderer, rd.textureData.texture, &rd.cliprect, &rd.target);
}

const string RenderSystem::getIdentifier() const {
	return "RenderSystem";
}

void RenderSystem::calculateZIndex(ID id) {
	auto& rc = componentManager->renderComponents.at(id);
	auto& mc = componentManager->moveComponents.at(id);
	rc.zindex = mc.pos.y + rc.textureData.height + rc.yoffset;
}

void RenderSystem::activateId(ID id) {
	//Only make the id active if it is a member of rendersystem
	if(ids.find(id) != ids.end()) {
		activeIds.push(id);
	}
}

void RenderSystem::setCameraSystem(CameraSystem* cameraSystem) {
	this->cameraSystem = cameraSystem;
}

void RenderSystem::setImage(ID id, string path) {
	auto& rc = componentManager->renderComponents.at(id);
	rc.imagePath = path;
	rc.textureData = textureDatas.at(path);
    activateId(id);
}

void RenderSystem::printText(const Text& text) {
    texts.push(text);
}

void RenderSystem::renderTexts() {
    //Deal with text. Pops texts from the 'texts'-queue
    //printing the texts using data from texts-class
    SDL_SetRenderTarget(renderer, fontTexture);
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
    SDL_RenderClear(renderer);
    while(!texts.empty()) {
        const Text text = texts.front(); texts.pop();
        //Put the fps on tmp (text->surface->tmp->fontTexture->default render target)
        auto fontSurface = TTF_RenderText_Solid(font, text.text.c_str(), text.color);
        auto tmpTexture = SDL_CreateTextureFromSurface(renderer, fontSurface);
        int w, h;
        TTF_SizeText(font, text.text.c_str(), &w, &h);
        const SDL_Rect tmpdest = {text.x, text.y, w, h};
        SDL_RenderCopy(renderer, tmpTexture, nullptr, &tmpdest);
        SDL_FreeSurface(fontSurface);
        SDL_DestroyTexture(tmpTexture);
    }
}
<commit_msg>added a dirty-fix to handle screen not showing up before whole world is blitted<commit_after>#include "rendersystem.hpp"
#include "componentmanager.hpp"
#include "rendercomponent.hpp"
#include "movecomponent.hpp"
#include "sizecomponent.hpp"
#include <iostream>
#include "spatialindexer.hpp"
#include "heap.hpp"
#include "dynamicarray.hpp"
#include "heapsort.hpp"
#include <queue>
#include "namecomponent.hpp"
#include "systemmanager.hpp"
#include "hashgridsystem.hpp"
#include "deltatime.hpp"
#include "camerasystem.hpp"

using namespace std;

bool RenderData::operator< (const RenderData& rhs) const {
    return
        renderComponent->zindex_base == rhs.renderComponent->zindex_base ?
        renderComponent->zindex < rhs.renderComponent->zindex :
        renderComponent->zindex_base < rhs.renderComponent->zindex_base;
}

bool RenderData::operator> (const RenderData& rhs) const { return rhs < *this; }
bool RenderData::operator<=(const RenderData& rhs) const { return !(*this > rhs); }
bool RenderData::operator>=(const RenderData& rhs) const { return !(*this < rhs); }

RenderSystem::RenderSystem() {
	//Initialize SDL
    if(SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
    {
        cout << "ERROR: SDL could not initialize! SDL_Error: " << SDL_GetError() << endl;
    } else {
        window = SDL_CreateWindow(
            "SDL Tutorial",
            SDL_WINDOWPOS_UNDEFINED,
            SDL_WINDOWPOS_UNDEFINED,
            SCREEN_WIDTH,
            SCREEN_HEIGHT,
            SDL_WINDOW_SHOWN);

        if(!window) {
            cout << "ERROR: Window could not be created! SDL_Error: " << SDL_GetError() << endl;
        } else {
            renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
            if(!renderer) {
                cout << "ERROR: Renderer could not be created! SDL_Error: " << SDL_GetError() << endl;
            } else {

				//imgFlags is used to initialize PNG-loading
				//so that IMG_Load can be used instead of SDL_LoadBMP
				int imgFlags = IMG_INIT_PNG;
				if(!(IMG_Init(imgFlags) & imgFlags)) {
					cout << "ERROR: Couldnt initialze PNG usage!" << endl;
				} else {

		            SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF);

                    fontTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, SCREEN_WIDTH, SCREEN_HEIGHT);
                    if(!fontTexture) {
						cout << "ERROR: Couldn't create fontTexture!" << endl;
						cout << "Screen will probably go black from now on" << endl;
						cout << "SDL_GetError(): " << SDL_GetError() << endl;
					}
                    SDL_SetTextureBlendMode(fontTexture, SDL_BLENDMODE_BLEND);

			        worldTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 8192, 8192);
					if(!worldTexture) {
						cout << "ERROR: Couldn't create worldTexture!" << endl;
						cout << "Screen will probably go black from now on" << endl;
						cout << "SDL_GetError(): " << SDL_GetError() << endl;
					}
					SDL_SetRenderTarget(renderer, worldTexture);
					SDL_RenderClear(renderer);
				}

                if(TTF_Init() < 0 ) {
                    cout << "ERROR: Couldn't initialize SDL2_ttf!" << endl;
                } else {
                    font = TTF_OpenFont("/usr/share/fonts/TTF/DejaVuSansMono.ttf", 11);
                    if(!font) {
                        cout << "ERROR: Failed to load font!" << endl;
                    }
                }
            }
        }
    }

    //We want to access surfaces by their image path later on, so store paths right away
    for(auto path :{
            "./resources/images/HelloWorld.bmp",
            "./resources/images/player.bmp",
            "./resources/images/grass.png",
			"./resources/images/SmallTree.png",
            "./resources/images/playerv2.png",

            "./resources/images/playerv3_front.png",
            "./resources/images/playerv3_back.png",
            "./resources/images/playerv3_left.png",
            "./resources/images/playerv3_right.png",

            "./resources/images/playerv3_front_run1.png",
            "./resources/images/playerv3_front_run2.png",
            "./resources/images/playerv3_front_run3.png",
            "./resources/images/playerv3_front_run4.png",

            "./resources/images/bloodsplatter1_1.png",
            "./resources/images/bloodsplatter1_2.png",
            "./resources/images/bloodsplatter1_3.png",
            "./resources/images/bloodsplatter1_4.png",
            "./resources/images/bloodsplatter1_5.png",
            "./resources/images/bloodsplatter1_6.png",
            "./resources/images/bloodsplatter1_7.png",
            "./resources/images/bloodsplatter1_8.png",
            "./resources/images/bloodsplatter1_9.png",
            "./resources/images/bloodsplatter1_10.png",
            "./resources/images/bloodsplatter1_11.png",

            "./resources/images/yellow_flower.png",
            "./resources/images/green_flower.png",
            "./resources/images/violet_flower.png",
            "./resources/images/blue_flower.png",
            }
        )
    {
        SDL_Surface* rawImage = IMG_Load(path);
        if(!rawImage) {
            cout << "ERROR: Couldn't load image on: " << path << endl;
        } else {
            //Make purple parts of images transparent by using color keying
            SDL_SetColorKey(rawImage, SDL_TRUE, SDL_MapRGB( rawImage->format, 0xFF, 0x00, 0xFF ));
            SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, rawImage);
            if(!texture) {
                cout << "ERROR: Couldn't convert surface on addr " << rawImage << " to texture! SDL_Error: " << SDL_GetError() << endl;
            } else {
                SDL_FreeSurface(rawImage);
                int w, h;
                SDL_QueryTexture(texture, NULL, NULL, &w, &h);
                textureDatas.insert(
                    make_pair(
                        path,
                        TextureData{
                            texture,
                            (unsigned int)w,
                            (unsigned int)h
                        }
                    )
                );
            }
        }
    }
}

RenderSystem::~RenderSystem() {
    TTF_CloseFont(font);
    TTF_Quit();

    //Free all loaded images
    for(auto textureData : textureDatas) {
        SDL_DestroyTexture(get<1>(textureData).texture);
    }

    SDL_DestroyTexture(fontTexture);
    SDL_DestroyTexture(worldTexture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_QuitSubSystem(SDL_INIT_VIDEO);
}

void RenderSystem::add(ID id) {
	ids.insert(id);
	activeIds.push(id);
}

void RenderSystem::remove(ID id) {
	ids.erase(id);
}

void RenderSystem::update() {
	heap<RenderData> pq; //TODO: Use reference instead (no need for copying!)
	auto spatialIndexer = dynamic_cast<SpatialIndexer*>(systemManager->getSystem("TextureHashGridSystem"));

    //Will contain all rectangles where a redraw is required
    queue<SpatialIndexer::Rect> drawQueue;

    //All drawareas saved from previous frame should be drawn this frame
	drawQueue.swap(previousDrawAreas);

    //Cap the amount of IDs getting rendered each frame
    //this is only a quick-fix to ensure no program-freeze
    //when initially rendering the whole world
    char allowedRendersThisFrame = 32;
	//For all activeIds in rendersystem...
	while(!activeIds.empty() && allowedRendersThisFrame-- > 0 ) {
		//Draw everything within the activeIds texturearea
        auto drawArea = spatialIndexer->getBoundingBox(activeIds.front());
		drawQueue.push(drawArea); activeIds.pop();

        //The texturearea should be saved for next frame, it must be redrawn then too
		renderArea(drawArea);
	}

    while(!drawQueue.empty()) {
        const auto& drawArea = drawQueue.front();

        //Make sure all overlapping ids have their part in the intersection area
    	//are redrawn
    	auto spatialIndexer = dynamic_cast<SpatialIndexer*>(systemManager->getSystem("TextureHashGridSystem"));

    	for(auto id : spatialIndexer->query(drawArea)) {
    		auto& mc = componentManager->moveComponents.at(id);
    		auto& rc = componentManager->renderComponents.at(id);

    		//Get the intersection between an entity within drawarea and the drawarea
    		const auto intersectionArea = spatialIndexer->getIntersectionArea(drawArea, spatialIndexer->getBoundingBox(id));

    		//Only draw that portion of the texture that intersects
    		calculateZIndex(id);
    		pq.insert({id, &rc, rc.textureData, {
    				(int)(intersectionArea.x - mc.pos.x - rc.xoffset),
    				(int)(intersectionArea.y - mc.pos.y - rc.yoffset),
    				(int)intersectionArea.w,
    				(int)intersectionArea.h }, {
    				(int)intersectionArea.x,
    				(int)intersectionArea.y,
    				(int)intersectionArea.w,
    				(int)intersectionArea.h}

    				}
    			);
    	}

        //Dont move this piece of code. A reference to this element is used above!
        drawQueue.pop();
    }

    //Draw to worldTexture
	SDL_SetRenderTarget(renderer, worldTexture);

	while(!pq.isEmpty()) {
		render(pq.poll());
	}

    //Put the fps on tmp (text->surface->tmp->fontTexture->default render target)
    const std::string str = std::to_string((int)(1/deltaTime->delta())) + "fps";
    printText(Text(str, 0, 0, {231, 195, 175}));

    //If all active ids werent rendered this frame, warn
    if(allowedRendersThisFrame <= 0) {
        const std::string str = "WARNING: renderer is lagging behind";
        printText(Text(str, 0, 20, {255, 55, 55}));
    }

    //Render all texts
    renderTexts();

    //Draw to default render target (NULL)
    SDL_SetRenderTarget(renderer, NULL);
    SDL_RenderClear(renderer);
    SDL_RenderCopy(renderer, worldTexture, &cameraSystem->getCamera(), NULL);
    SDL_RenderCopy(renderer, fontTexture, nullptr, nullptr);
	SDL_RenderPresent(renderer);
}

unsigned int RenderSystem::count() const {
	return ids.size();
}

void RenderSystem::render(const RenderData& rd) const {
    SDL_RenderCopy(renderer, rd.textureData.texture, &rd.cliprect, &rd.target);
}

const string RenderSystem::getIdentifier() const {
	return "RenderSystem";
}

void RenderSystem::calculateZIndex(ID id) {
	auto& rc = componentManager->renderComponents.at(id);
	auto& mc = componentManager->moveComponents.at(id);
	rc.zindex = mc.pos.y + rc.textureData.height + rc.yoffset;
}

void RenderSystem::activateId(ID id) {
	//Only make the id active if it is a member of rendersystem
	if(ids.find(id) != ids.end()) {
		activeIds.push(id);
	}
}

void RenderSystem::setCameraSystem(CameraSystem* cameraSystem) {
	this->cameraSystem = cameraSystem;
}

void RenderSystem::setImage(ID id, string path) {
	auto& rc = componentManager->renderComponents.at(id);
	rc.imagePath = path;
	rc.textureData = textureDatas.at(path);
    activateId(id);
}

void RenderSystem::printText(const Text& text) {
    texts.push(text);
}

void RenderSystem::renderTexts() {
    //Deal with text. Pops texts from the 'texts'-queue
    //printing the texts using data from texts-class
    SDL_SetRenderTarget(renderer, fontTexture);
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
    SDL_RenderClear(renderer);
    while(!texts.empty()) {
        const Text text = texts.front(); texts.pop();
        //Put the fps on tmp (text->surface->tmp->fontTexture->default render target)
        auto fontSurface = TTF_RenderText_Solid(font, text.text.c_str(), text.color);
        auto tmpTexture = SDL_CreateTextureFromSurface(renderer, fontSurface);
        int w, h;
        TTF_SizeText(font, text.text.c_str(), &w, &h);
        const SDL_Rect tmpdest = {text.x, text.y, w, h};
        SDL_RenderCopy(renderer, tmpTexture, nullptr, &tmpdest);
        SDL_FreeSurface(fontSurface);
        SDL_DestroyTexture(tmpTexture);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2016, Matt Godbolt
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include "seasocks/Server.h"
#include "seasocks/Connection.h"
#include "seasocks/IgnoringLogger.h"

#include "catch.hpp"

#include <thread>

using namespace seasocks;

TEST_CASE("Server tests", "[ServerTests]") {
    auto logger = std::make_shared<IgnoringLogger>();
    Server server(logger);
    REQUIRE(server.startListening(0));
    std::thread seasocksThread([&]{
        REQUIRE(server.loop());
    });

    std::atomic<int> test(0);
    SECTION("execute should work") {
        server.execute([&]{
            CHECK(test == 0);
            test++;
        });
        for (int i = 0; i < 1000 * 1000 * 1000; ++i) {
            if (test) break;
        }
        CHECK(test == 1);
    }

    server.terminate();
    seasocksThread.join();
}<commit_msg>Couple more server tests<commit_after>// Copyright (c) 2013-2016, Matt Godbolt
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include "seasocks/Server.h"
#include "seasocks/Connection.h"
#include "seasocks/IgnoringLogger.h"

#include "catch.hpp"

#include <thread>
#include <unistd.h>

using namespace seasocks;

TEST_CASE("Server tests", "[ServerTests]") {
    auto logger = std::make_shared<IgnoringLogger>();
    Server server(logger);
    REQUIRE(server.startListening(0));
    std::thread seasocksThread([&]{
        REQUIRE(server.loop());
    });

    std::atomic<int> test(0);
    SECTION("execute should work") {
        server.execute([&]{
            CHECK(test == 0);
            test++;
        });
        for (int i = 0; i < 1000 * 1000 * 1000; ++i) {
            if (test) break;
        }
        CHECK(test == 1);
    }

    SECTION("many executes") {
        std::atomic<bool> latch(false);
        for (auto i = 0; i < 100; ++i) {
            for (auto i = 0; i < 100; ++i) {
                server.execute([&] { test++; });
            }
            usleep(10);
        }
        server.execute([&] { latch = true; });
        for (int i = 0; i < 1000; ++i) {
            usleep(1000);
            if (latch) break;
        }
        CHECK(latch == 1);
        CHECK(test == 10000);
    }

    server.terminate();
    seasocksThread.join();
}<|endoftext|>


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

#include <string>
#include <vector>

#include "netbase.h"

using namespace std;

BOOST_AUTO_TEST_SUITE(netbase_tests)

BOOST_AUTO_TEST_CASE(netbase_networks)
{
    BOOST_CHECK(CNetAddr("127.0.0.1").GetNetwork()                              == NET_UNROUTABLE);
    BOOST_CHECK(CNetAddr("::1").GetNetwork()                                    == NET_UNROUTABLE);
    BOOST_CHECK(CNetAddr("8.8.8.8").GetNetwork()                                == NET_IPV4);
    BOOST_CHECK(CNetAddr("2001::8888").GetNetwork()                             == NET_IPV6);
    BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_TOR);
}

BOOST_AUTO_TEST_CASE(netbase_properties)
{
    BOOST_CHECK(CNetAddr("127.0.0.1").IsIPv4());
    BOOST_CHECK(CNetAddr("::FFFF:192.168.1.1").IsIPv4());
    BOOST_CHECK(CNetAddr("::1").IsIPv6());
    BOOST_CHECK(CNetAddr("10.0.0.1").IsRFC1918());
    BOOST_CHECK(CNetAddr("192.168.1.1").IsRFC1918());
    BOOST_CHECK(CNetAddr("172.31.255.255").IsRFC1918());
    BOOST_CHECK(CNetAddr("2001:0DB8::").IsRFC3849());
    BOOST_CHECK(CNetAddr("169.254.1.1").IsRFC3927());
    BOOST_CHECK(CNetAddr("2002::1").IsRFC3964());
    BOOST_CHECK(CNetAddr("FC00::").IsRFC4193());
    BOOST_CHECK(CNetAddr("2001::2").IsRFC4380());
    BOOST_CHECK(CNetAddr("2001:10::").IsRFC4843());
    BOOST_CHECK(CNetAddr("FE80::").IsRFC4862());
    BOOST_CHECK(CNetAddr("64:FF9B::").IsRFC6052());
    BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").IsTor());
    BOOST_CHECK(CNetAddr("127.0.0.1").IsLocal());
    BOOST_CHECK(CNetAddr("::1").IsLocal());
    BOOST_CHECK(CNetAddr("8.8.8.8").IsRoutable());
    BOOST_CHECK(CNetAddr("2001::1").IsRoutable());
    BOOST_CHECK(CNetAddr("127.0.0.1").IsValid());
}

bool static TestSplitHost(string test, string host, int port)
{
    string hostOut;
    int portOut = -1;
    SplitHostPort(test, portOut, hostOut);
    return hostOut == host && port == portOut;
}

BOOST_AUTO_TEST_CASE(netbase_splithost)
{
    BOOST_CHECK(TestSplitHost("www.bitcoin.org", "www.bitcoin.org", -1));
    BOOST_CHECK(TestSplitHost("[www.bitcoin.org]", "www.bitcoin.org", -1));
    BOOST_CHECK(TestSplitHost("www.bitcoin.org:80", "www.bitcoin.org", 80));
    BOOST_CHECK(TestSplitHost("[www.bitcoin.org]:80", "www.bitcoin.org", 80));
    BOOST_CHECK(TestSplitHost("127.0.0.1", "127.0.0.1", -1));
    BOOST_CHECK(TestSplitHost("127.0.0.1:8333", "127.0.0.1", 8333));
    BOOST_CHECK(TestSplitHost("[127.0.0.1]", "127.0.0.1", -1));
    BOOST_CHECK(TestSplitHost("[127.0.0.1]:8333", "127.0.0.1", 8333));
    BOOST_CHECK(TestSplitHost("::ffff:127.0.0.1", "::ffff:127.0.0.1", -1));
    BOOST_CHECK(TestSplitHost("[::ffff:127.0.0.1]:8333", "::ffff:127.0.0.1", 8333));
    BOOST_CHECK(TestSplitHost("[::]:8333", "::", 8333));
    BOOST_CHECK(TestSplitHost("::8333", "::8333", -1));
    BOOST_CHECK(TestSplitHost(":8333", "", 8333));
    BOOST_CHECK(TestSplitHost("[]:8333", "", 8333));
    BOOST_CHECK(TestSplitHost("", "", -1));
}

bool static TestParse(string src, string canon)
{
    CService addr;
    if (!LookupNumeric(src.c_str(), addr, 65535))
        return canon == "";
    return canon == addr.ToString();
}

BOOST_AUTO_TEST_CASE(netbase_lookupnumeric)
{
    BOOST_CHECK(TestParse("127.0.0.1", "127.0.0.1:65535"));
    BOOST_CHECK(TestParse("127.0.0.1:8333", "127.0.0.1:8333"));
    BOOST_CHECK(TestParse("::ffff:127.0.0.1", "127.0.0.1:65535"));
    BOOST_CHECK(TestParse("::", "[::]:65535"));
    BOOST_CHECK(TestParse("[::]:8333", "[::]:8333"));
    BOOST_CHECK(TestParse("[127.0.0.1]", "127.0.0.1:65535"));
    BOOST_CHECK(TestParse(":::", ""));
}

BOOST_AUTO_TEST_CASE(onioncat_test)
{
    // values from http://www.cypherpunk.at/onioncat/wiki/OnionCat
    CNetAddr addr1("5wyqrzbvrdsumnok.onion");
    CNetAddr addr2("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca");
    BOOST_CHECK(addr1 == addr2);
    BOOST_CHECK(addr1.IsTor());
    BOOST_CHECK(addr1.ToStringIP() == "5wyqrzbvrdsumnok.onion");
    BOOST_CHECK(addr1.IsRoutable());
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>Update netbase_tests.cpp<commit_after>#include <boost/test/unit_test.hpp>

#include <string>
#include <vector>

#include "netbase.h"

using namespace std;

BOOST_AUTO_TEST_SUITE(netbase_tests)

BOOST_AUTO_TEST_CASE(netbase_networks)
{
    BOOST_CHECK(CNetAddr("127.0.0.1").GetNetwork()                              == NET_UNROUTABLE);
    BOOST_CHECK(CNetAddr("::1").GetNetwork()                                    == NET_UNROUTABLE);
    BOOST_CHECK(CNetAddr("8.8.8.8").GetNetwork()                                == NET_IPV4);
    BOOST_CHECK(CNetAddr("2001::9999").GetNetwork()                             == NET_IPV6);
    BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_TOR);
}

BOOST_AUTO_TEST_CASE(netbase_properties)
{
    BOOST_CHECK(CNetAddr("127.0.0.1").IsIPv4());
    BOOST_CHECK(CNetAddr("::FFFF:192.168.1.1").IsIPv4());
    BOOST_CHECK(CNetAddr("::1").IsIPv6());
    BOOST_CHECK(CNetAddr("10.0.0.1").IsRFC1918());
    BOOST_CHECK(CNetAddr("192.168.1.1").IsRFC1918());
    BOOST_CHECK(CNetAddr("172.31.255.255").IsRFC1918());
    BOOST_CHECK(CNetAddr("2001:0DB8::").IsRFC3849());
    BOOST_CHECK(CNetAddr("169.254.1.1").IsRFC3927());
    BOOST_CHECK(CNetAddr("2002::1").IsRFC3964());
    BOOST_CHECK(CNetAddr("FC00::").IsRFC4193());
    BOOST_CHECK(CNetAddr("2001::2").IsRFC4380());
    BOOST_CHECK(CNetAddr("2001:10::").IsRFC4843());
    BOOST_CHECK(CNetAddr("FE80::").IsRFC4862());
    BOOST_CHECK(CNetAddr("64:FF9B::").IsRFC6052());
    BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").IsTor());
    BOOST_CHECK(CNetAddr("127.0.0.1").IsLocal());
    BOOST_CHECK(CNetAddr("::1").IsLocal());
    BOOST_CHECK(CNetAddr("8.8.8.8").IsRoutable());
    BOOST_CHECK(CNetAddr("2001::1").IsRoutable());
    BOOST_CHECK(CNetAddr("127.0.0.1").IsValid());
}

bool static TestSplitHost(string test, string host, int port)
{
    string hostOut;
    int portOut = -1;
    SplitHostPort(test, portOut, hostOut);
    return hostOut == host && port == portOut;
}

BOOST_AUTO_TEST_CASE(netbase_splithost)
{
    BOOST_CHECK(TestSplitHost("www.bitcoin.org", "www.bitcoin.org", -1));
    BOOST_CHECK(TestSplitHost("[www.bitcoin.org]", "www.bitcoin.org", -1));
    BOOST_CHECK(TestSplitHost("www.bitcoin.org:80", "www.bitcoin.org", 80));
    BOOST_CHECK(TestSplitHost("[www.bitcoin.org]:80", "www.bitcoin.org", 80));
    BOOST_CHECK(TestSplitHost("127.0.0.1", "127.0.0.1", -1));
    BOOST_CHECK(TestSplitHost("127.0.0.1:8333", "127.0.0.1", 8333));
    BOOST_CHECK(TestSplitHost("[127.0.0.1]", "127.0.0.1", -1));
    BOOST_CHECK(TestSplitHost("[127.0.0.1]:8333", "127.0.0.1", 8333));
    BOOST_CHECK(TestSplitHost("::ffff:127.0.0.1", "::ffff:127.0.0.1", -1));
    BOOST_CHECK(TestSplitHost("[::ffff:127.0.0.1]:8333", "::ffff:127.0.0.1", 8333));
    BOOST_CHECK(TestSplitHost("[::]:8333", "::", 8333));
    BOOST_CHECK(TestSplitHost("::8333", "::8333", -1));
    BOOST_CHECK(TestSplitHost(":8333", "", 8333));
    BOOST_CHECK(TestSplitHost("[]:8333", "", 8333));
    BOOST_CHECK(TestSplitHost("", "", -1));
}

bool static TestParse(string src, string canon)
{
    CService addr;
    if (!LookupNumeric(src.c_str(), addr, 65535))
        return canon == "";
    return canon == addr.ToString();
}

BOOST_AUTO_TEST_CASE(netbase_lookupnumeric)
{
    BOOST_CHECK(TestParse("127.0.0.1", "127.0.0.1:65535"));
    BOOST_CHECK(TestParse("127.0.0.1:8333", "127.0.0.1:8333"));
    BOOST_CHECK(TestParse("::ffff:127.0.0.1", "127.0.0.1:65535"));
    BOOST_CHECK(TestParse("::", "[::]:65535"));
    BOOST_CHECK(TestParse("[::]:8333", "[::]:8333"));
    BOOST_CHECK(TestParse("[127.0.0.1]", "127.0.0.1:65535"));
    BOOST_CHECK(TestParse(":::", ""));
}

BOOST_AUTO_TEST_CASE(onioncat_test)
{
    // values from http://www.cypherpunk.at/onioncat/wiki/OnionCat
    CNetAddr addr1("5wyqrzbvrdsumnok.onion");
    CNetAddr addr2("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca");
    BOOST_CHECK(addr1 == addr2);
    BOOST_CHECK(addr1.IsTor());
    BOOST_CHECK(addr1.ToStringIP() == "5wyqrzbvrdsumnok.onion");
    BOOST_CHECK(addr1.IsRoutable());
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


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

#include <string>
#include <vector>

#include "netbase.h"

using namespace std;

BOOST_AUTO_TEST_SUITE(netbase_tests)

BOOST_AUTO_TEST_CASE(netbase_networks)
{
    BOOST_CHECK(CNetAddr("127.0.0.1").GetNetwork()                              == NET_UNROUTABLE);
    BOOST_CHECK(CNetAddr("::1").GetNetwork()                                    == NET_UNROUTABLE);
    BOOST_CHECK(CNetAddr("8.8.8.8").GetNetwork()                                == NET_IPV4);
    BOOST_CHECK(CNetAddr("2001::8888").GetNetwork()                             == NET_IPV6);
    BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_TOR);
}

BOOST_AUTO_TEST_CASE(netbase_properties)
{
    BOOST_CHECK(CNetAddr("127.0.0.1").IsIPv4());
    BOOST_CHECK(CNetAddr("::FFFF:192.168.1.1").IsIPv4());
    BOOST_CHECK(CNetAddr("::1").IsIPv6());
    BOOST_CHECK(CNetAddr("10.0.0.1").IsRFC1918());
    BOOST_CHECK(CNetAddr("192.168.1.1").IsRFC1918());
    BOOST_CHECK(CNetAddr("172.31.255.255").IsRFC1918());
    BOOST_CHECK(CNetAddr("2001:0DB8::").IsRFC3849());
    BOOST_CHECK(CNetAddr("169.254.1.1").IsRFC3927());
    BOOST_CHECK(CNetAddr("2002::1").IsRFC3964());
    BOOST_CHECK(CNetAddr("FC00::").IsRFC4193());
    BOOST_CHECK(CNetAddr("2001::2").IsRFC4380());
    BOOST_CHECK(CNetAddr("2001:10::").IsRFC4843());
    BOOST_CHECK(CNetAddr("FE80::").IsRFC4862());
    BOOST_CHECK(CNetAddr("64:FF9B::").IsRFC6052());
    BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").IsTor());
    BOOST_CHECK(CNetAddr("127.0.0.1").IsLocal());
    BOOST_CHECK(CNetAddr("::1").IsLocal());
    BOOST_CHECK(CNetAddr("8.8.8.8").IsRoutable());
    BOOST_CHECK(CNetAddr("2001::1").IsRoutable());
    BOOST_CHECK(CNetAddr("127.0.0.1").IsValid());
}

bool static TestSplitHost(string test, string host, int port)
{
    string hostOut;
    int portOut = -1;
    SplitHostPort(test, portOut, hostOut);
    return hostOut == host && port == portOut;
}

BOOST_AUTO_TEST_CASE(netbase_splithost)
{
    BOOST_CHECK(TestSplitHost("www.bitcoin.org", "www.bitcoin.org", -1));
    BOOST_CHECK(TestSplitHost("[www.bitcoin.org]", "www.bitcoin.org", -1));
    BOOST_CHECK(TestSplitHost("www.bitcoin.org:80", "www.bitcoin.org", 80));
    BOOST_CHECK(TestSplitHost("[www.bitcoin.org]:80", "www.bitcoin.org", 80));
    BOOST_CHECK(TestSplitHost("127.0.0.1", "127.0.0.1", -1));
    BOOST_CHECK(TestSplitHost("127.0.0.1:8333", "127.0.0.1", 8333));
    BOOST_CHECK(TestSplitHost("[127.0.0.1]", "127.0.0.1", -1));
    BOOST_CHECK(TestSplitHost("[127.0.0.1]:8333", "127.0.0.1", 8333));
    BOOST_CHECK(TestSplitHost("::ffff:127.0.0.1", "::ffff:127.0.0.1", -1));
    BOOST_CHECK(TestSplitHost("[::ffff:127.0.0.1]:8333", "::ffff:127.0.0.1", 8333));
    BOOST_CHECK(TestSplitHost("[::]:8333", "::", 8333));
    BOOST_CHECK(TestSplitHost("::8333", "::8333", -1));
    BOOST_CHECK(TestSplitHost(":8333", "", 8333));
    BOOST_CHECK(TestSplitHost("[]:8333", "", 8333));
    BOOST_CHECK(TestSplitHost("", "", -1));
}

bool static TestParse(string src, string canon)
{
    CService addr;
    if (!LookupNumeric(src.c_str(), addr, 65535))
        return canon == "";
    return canon == addr.ToString();
}

BOOST_AUTO_TEST_CASE(netbase_lookupnumeric)
{
    BOOST_CHECK(TestParse("127.0.0.1", "127.0.0.1:65535"));
    BOOST_CHECK(TestParse("127.0.0.1:8333", "127.0.0.1:8333"));
    BOOST_CHECK(TestParse("::ffff:127.0.0.1", "127.0.0.1:65535"));
    BOOST_CHECK(TestParse("::", "[::]:65535"));
    BOOST_CHECK(TestParse("[::]:8333", "[::]:8333"));
    BOOST_CHECK(TestParse("[127.0.0.1]", "127.0.0.1:65535"));
    BOOST_CHECK(TestParse(":::", ""));
}

BOOST_AUTO_TEST_CASE(onioncat_test)
{
    // values from http://www.cypherpunk.at/onioncat/wiki/OnionCat
    CNetAddr addr1("5wyqrzbvrdsumnok.onion");
    CNetAddr addr2("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca");
    BOOST_CHECK(addr1 == addr2);
    BOOST_CHECK(addr1.IsTor());
    BOOST_CHECK(addr1.ToStringIP() == "5wyqrzbvrdsumnok.onion");
    BOOST_CHECK(addr1.IsRoutable());
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>Use litecoin.org for unit tests<commit_after>#include <boost/test/unit_test.hpp>

#include <string>
#include <vector>

#include "netbase.h"

using namespace std;

BOOST_AUTO_TEST_SUITE(netbase_tests)

BOOST_AUTO_TEST_CASE(netbase_networks)
{
    BOOST_CHECK(CNetAddr("127.0.0.1").GetNetwork()                              == NET_UNROUTABLE);
    BOOST_CHECK(CNetAddr("::1").GetNetwork()                                    == NET_UNROUTABLE);
    BOOST_CHECK(CNetAddr("8.8.8.8").GetNetwork()                                == NET_IPV4);
    BOOST_CHECK(CNetAddr("2001::8888").GetNetwork()                             == NET_IPV6);
    BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetNetwork() == NET_TOR);
}

BOOST_AUTO_TEST_CASE(netbase_properties)
{
    BOOST_CHECK(CNetAddr("127.0.0.1").IsIPv4());
    BOOST_CHECK(CNetAddr("::FFFF:192.168.1.1").IsIPv4());
    BOOST_CHECK(CNetAddr("::1").IsIPv6());
    BOOST_CHECK(CNetAddr("10.0.0.1").IsRFC1918());
    BOOST_CHECK(CNetAddr("192.168.1.1").IsRFC1918());
    BOOST_CHECK(CNetAddr("172.31.255.255").IsRFC1918());
    BOOST_CHECK(CNetAddr("2001:0DB8::").IsRFC3849());
    BOOST_CHECK(CNetAddr("169.254.1.1").IsRFC3927());
    BOOST_CHECK(CNetAddr("2002::1").IsRFC3964());
    BOOST_CHECK(CNetAddr("FC00::").IsRFC4193());
    BOOST_CHECK(CNetAddr("2001::2").IsRFC4380());
    BOOST_CHECK(CNetAddr("2001:10::").IsRFC4843());
    BOOST_CHECK(CNetAddr("FE80::").IsRFC4862());
    BOOST_CHECK(CNetAddr("64:FF9B::").IsRFC6052());
    BOOST_CHECK(CNetAddr("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").IsTor());
    BOOST_CHECK(CNetAddr("127.0.0.1").IsLocal());
    BOOST_CHECK(CNetAddr("::1").IsLocal());
    BOOST_CHECK(CNetAddr("8.8.8.8").IsRoutable());
    BOOST_CHECK(CNetAddr("2001::1").IsRoutable());
    BOOST_CHECK(CNetAddr("127.0.0.1").IsValid());
}

bool static TestSplitHost(string test, string host, int port)
{
    string hostOut;
    int portOut = -1;
    SplitHostPort(test, portOut, hostOut);
    return hostOut == host && port == portOut;
}

BOOST_AUTO_TEST_CASE(netbase_splithost)
{
    BOOST_CHECK(TestSplitHost("www.litecoin.org", "www.litecoin.org", -1));
    BOOST_CHECK(TestSplitHost("[www.litecoin.org]", "www.litecoin.org", -1));
    BOOST_CHECK(TestSplitHost("www.litecoin.org:80", "www.litecoin.org", 80));
    BOOST_CHECK(TestSplitHost("[www.litecoin.org]:80", "www.litecoin.org", 80));
    BOOST_CHECK(TestSplitHost("127.0.0.1", "127.0.0.1", -1));
    BOOST_CHECK(TestSplitHost("127.0.0.1:8333", "127.0.0.1", 8333));
    BOOST_CHECK(TestSplitHost("[127.0.0.1]", "127.0.0.1", -1));
    BOOST_CHECK(TestSplitHost("[127.0.0.1]:8333", "127.0.0.1", 8333));
    BOOST_CHECK(TestSplitHost("::ffff:127.0.0.1", "::ffff:127.0.0.1", -1));
    BOOST_CHECK(TestSplitHost("[::ffff:127.0.0.1]:8333", "::ffff:127.0.0.1", 8333));
    BOOST_CHECK(TestSplitHost("[::]:8333", "::", 8333));
    BOOST_CHECK(TestSplitHost("::8333", "::8333", -1));
    BOOST_CHECK(TestSplitHost(":8333", "", 8333));
    BOOST_CHECK(TestSplitHost("[]:8333", "", 8333));
    BOOST_CHECK(TestSplitHost("", "", -1));
}

bool static TestParse(string src, string canon)
{
    CService addr;
    if (!LookupNumeric(src.c_str(), addr, 65535))
        return canon == "";
    return canon == addr.ToString();
}

BOOST_AUTO_TEST_CASE(netbase_lookupnumeric)
{
    BOOST_CHECK(TestParse("127.0.0.1", "127.0.0.1:65535"));
    BOOST_CHECK(TestParse("127.0.0.1:8333", "127.0.0.1:8333"));
    BOOST_CHECK(TestParse("::ffff:127.0.0.1", "127.0.0.1:65535"));
    BOOST_CHECK(TestParse("::", "[::]:65535"));
    BOOST_CHECK(TestParse("[::]:8333", "[::]:8333"));
    BOOST_CHECK(TestParse("[127.0.0.1]", "127.0.0.1:65535"));
    BOOST_CHECK(TestParse(":::", ""));
}

BOOST_AUTO_TEST_CASE(onioncat_test)
{
    // values from http://www.cypherpunk.at/onioncat/wiki/OnionCat
    CNetAddr addr1("5wyqrzbvrdsumnok.onion");
    CNetAddr addr2("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca");
    BOOST_CHECK(addr1 == addr2);
    BOOST_CHECK(addr1.IsTor());
    BOOST_CHECK(addr1.ToStringIP() == "5wyqrzbvrdsumnok.onion");
    BOOST_CHECK(addr1.IsRoutable());
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>/*********************************************************************
 * node-dvbtee for Node.js
 *
 * Copyright (c) 2017 Michael Ira Krufky <https://github.com/mkrufky>
 *
 * MIT License <https://github.com/mkrufky/node-dvbtee/blob/master/LICENSE>
 *
 * See https://github.com/mkrufky/node-dvbtee for more information
 ********************************************************************/

#include <nan.h>
#include "dvbtee-parser.h"  // NOLINT(build/include)

Nan::Persistent<v8::Function> dvbteeParser::constructor;

dvbteeParser::dvbteeParser() {
}

dvbteeParser::~dvbteeParser() {
}

void dvbteeParser::Init(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE exports) {
  Nan::HandleScope scope;

  v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
  tpl->SetClassName(Nan::New("Parser").ToLocalChecked());
  tpl->InstanceTemplate()->SetInternalFieldCount(1);

  Nan::SetPrototypeMethod(tpl, "reset",        reset);
  Nan::SetPrototypeMethod(tpl, "feed",         feed);
  Nan::SetPrototypeMethod(tpl, "enableEttCollection", enableEttCollection);

  constructor.Reset(tpl->GetFunction());
  exports->Set(Nan::New("Parser").ToLocalChecked(), tpl->GetFunction());
}

void dvbteeParser::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  if (info.IsConstructCall()) {
    // Invoked as constructor: `new dvbteeParser(...)`
    dvbteeParser* obj = new dvbteeParser();
    obj->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  } else {
    // Invoked as plain function `dvbteeParser(...)`, turn into construct call.
    const int argc = 0;
    v8::Local<v8::Value> argv[argc] = { };
    v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);
    info.GetReturnValue().Set(cons->NewInstance(argc, argv));
  }
}

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

void dvbteeParser::enableEttCollection(
  const Nan::FunctionCallbackInfo<v8::Value>& info) {
  dvbteeParser* obj = ObjectWrap::Unwrap<dvbteeParser>(info.Holder());

  obj->m_parser.enable_ett_collection();

  info.GetReturnValue().Set(info.Holder());
}

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

class ResetWorker : public Nan::AsyncWorker {
 public:
  ResetWorker(Nan::Callback *callback,
              const Nan::FunctionCallbackInfo<v8::Value>& info)
    : Nan::AsyncWorker(callback) {
      m_obj = Nan::ObjectWrap::Unwrap<dvbteeParser>(info.Holder());
    }
  ~ResetWorker() {
    }

  void Execute () {
    m_obj->m_parser.cleanup();
    m_obj->m_parser.reset();
  }

  void HandleOKCallback () {
    Nan::HandleScope scope;

    v8::Local<v8::Value> argv[] = {
        Nan::Null()
      , Nan::New<v8::Number>(0)
    };

    callback->Call(2, argv);
  }

 private:
  dvbteeParser* m_obj;
};

void dvbteeParser::reset(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  // If a callback function is supplied, this method will run async
  // otherwise, it will run synchronous

  int lastArg = info.Length() - 1;

  if ((lastArg >= 0) && (info[lastArg]->IsFunction())) {
    Nan::Callback *callback = new Nan::Callback(
      info[lastArg].As<v8::Function>()
    );
    Nan::AsyncQueueWorker(new ResetWorker(callback, info));

  } else {
    dvbteeParser* obj = ObjectWrap::Unwrap<dvbteeParser>(info.Holder());

    obj->m_parser.cleanup();
    obj->m_parser.reset();

    info.GetReturnValue().Set(info.Holder());
  }
}

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

namespace KrufkyNan {

using namespace Nan;

template<class T, typename... Targs>
/* abstract */ class AsyncBareFactoryWorker : public AsyncWorker {
 public:
  explicit AsyncBareFactoryWorker(Callback *callback_)
      : AsyncWorker(callback_) {
    async = new uv_async_t;
    uv_async_init(
        uv_default_loop()
      , async
      , AsyncProgress_
    );
    async->data = this;
  }

  virtual ~AsyncBareFactoryWorker() {
  }

  virtual void WorkProgress() = 0;

  class ExecutionProgress {
    friend class AsyncBareFactoryWorker;
   public:
    void Signal() const {
        uv_async_send(that_->async);
    }

    void Construct(Targs... Fargs) const {
        that_->ConstructProgress_(Fargs...);
    }

   private:
    explicit ExecutionProgress(AsyncBareFactoryWorker *that) : that_(that) {}
    NAN_DISALLOW_ASSIGN_COPY_MOVE(ExecutionProgress)
    AsyncBareFactoryWorker* const that_;
  };

  virtual void Execute(const ExecutionProgress& progress) = 0;
  virtual void HandleProgressCallback(const T *data, size_t size) = 0;

  virtual void Destroy() {
      uv_close(reinterpret_cast<uv_handle_t*>(async), AsyncClose_);
  }

 private:
  void Execute() /*final override*/ {
      ExecutionProgress progress(this);
      Execute(progress);
  }

  virtual void ConstructProgress_(Targs... Fargs) = 0;

  inline static NAUV_WORK_CB(AsyncProgress_) {
    AsyncBareFactoryWorker *worker =
            static_cast<AsyncBareFactoryWorker*>(async->data);
    worker->WorkProgress();
  }

  inline static void AsyncClose_(uv_handle_t* handle) {
    AsyncBareFactoryWorker *worker =
            static_cast<AsyncBareFactoryWorker*>(handle->data);
    delete reinterpret_cast<uv_async_t*>(handle);
    delete worker;
  }

 protected:
  uv_async_t *async;
};

template<class T, typename... Targs>
/* abstract */
class AsyncFactoryWorker : public AsyncBareFactoryWorker<T, Targs...> {
 public:
  explicit AsyncFactoryWorker(Callback *callback_)
      : AsyncBareFactoryWorker<T, Targs...>(callback_) {
    uv_mutex_init(&async_lock);
  }

  virtual ~AsyncFactoryWorker() {
    uv_mutex_lock(&async_lock);

    while (!asyncdata_.empty()) {
      T *data = asyncdata_.front();

      asyncdata_.pop();

      delete data;
    }

    uv_mutex_unlock(&async_lock);
    uv_mutex_destroy(&async_lock);
  }

  void WorkComplete() {
    WorkProgress();
    AsyncWorker::WorkComplete();
  }

  void WorkProgress() {
    uv_mutex_lock(&async_lock);

    while (!asyncdata_.empty()) {
      T *data = asyncdata_.front();
      asyncdata_.pop();
      uv_mutex_unlock(&async_lock);

      // Don't send progress events after we've already completed.
      if (this->callback) {
          this->HandleProgressCallback(data, 1);
      }

      delete data;

      uv_mutex_lock(&async_lock);
    }

    uv_mutex_unlock(&async_lock);
  }

 private:
  void ConstructProgress_(Targs... Fargs) {
    T *data = new T(Fargs...);

    uv_mutex_lock(&async_lock);
    asyncdata_.push(data);
    uv_mutex_unlock(&async_lock);

    uv_async_send(this->async);
  }

  uv_mutex_t async_lock;
  std::queue<T*> asyncdata_;
};

}  // namespace KrufkyNan

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

class TableData {
 public:
  explicit TableData(
  const uint8_t& tableId,
  const std::string& decoderName,
  const std::string& json)
  : tableId(tableId)
  , decoderName(decoderName)
  , json(json) {
  }

  const uint8_t tableId;
  const std::string decoderName;
  const std::string json;

 private:
  TableData() = delete;
  TableData(const TableData&) = delete;
  TableData& operator= (const TableData&) = delete;
};

class FeedWorker: public KrufkyNan::AsyncFactoryWorker<TableData, const uint8_t&, const std::string&, const std::string&> {
 public:
  FeedWorker(Nan::Callback *callback,
             Nan::Callback *progress,
             const Nan::FunctionCallbackInfo<v8::Value>& info)
    : AsyncFactoryWorker<TableData, const uint8_t&, const std::string&, const std::string&>(callback)
    , m_progress(progress)
    , m_buf(NULL)
    , m_buf_len(0)
    , m_ret(-1) {
      m_obj = Nan::ObjectWrap::Unwrap<dvbteeParser>(info.Holder());

      if ((info[0]->IsObject()) && (info[1]->IsNumber())) {
        const char *key = "buf";
        SaveToPersistent(key, info[0]);
        Nan::MaybeLocal<v8::Object> mbBuf =
          Nan::To<v8::Object>(GetFromPersistent(key));

        if (!mbBuf.IsEmpty()) {
          m_buf = node::Buffer::Data(mbBuf.ToLocalChecked());
          m_buf_len = info[1]->Uint32Value();
        }
      }
    }
  ~FeedWorker() {
    }

  void Execute(const AsyncFactoryWorker::ExecutionProgress& progress) {
    if ((m_buf) && (m_buf_len)) {
      Watcher watch(this->m_obj, progress);

      m_ret = m_obj->m_parser.feed(
        m_buf_len, reinterpret_cast<uint8_t*>(m_buf)
      );
    }
    if (m_ret < 0) {
      SetErrorMessage("invalid buffer / length");
    }
  }

  void HandleProgressCallback(const TableData *async_data, size_t count) {
    Nan::HandleScope scope;

    if (!m_progress->IsEmpty()) {
      for (unsigned int i = 0; i < count; i++) {
        const TableData *data = &async_data[i];

        Nan::MaybeLocal<v8::String> jsonStr = Nan::New(data->json);
        if (!jsonStr.IsEmpty()) {
          Nan::MaybeLocal<v8::Value> jsonVal =
            m_obj->NanJSON.Parse(jsonStr.ToLocalChecked());

          if (!jsonVal.IsEmpty()) {
            Nan::MaybeLocal<v8::String> decoderName =
              Nan::New(data->decoderName);

            v8::Local<v8::Value> decoderNameArg;
            if (decoderName.IsEmpty())
              decoderNameArg = Nan::Null();
            else
              decoderNameArg = decoderName.ToLocalChecked();

            v8::Local<v8::Value> argv[] = {
              Nan::New(data->tableId),
              decoderNameArg,
              jsonVal.ToLocalChecked()
            };

            m_progress->Call(3, argv);
          }
        }
      }
    }
  }

  void HandleOKCallback() {
    Nan::HandleScope scope;

    v8::Local<v8::Value> argv[] = {
        Nan::Null()
      , Nan::New<v8::Number>(m_ret)
    };

    callback->Call(2, argv);
  }

 private:
  Nan::Callback *m_progress;
  dvbteeParser* m_obj;
  char* m_buf;
  unsigned int m_buf_len;
  int m_ret;

  class Watcher: public dvbtee::decode::TableWatcher {
   public:
    explicit Watcher(dvbteeParser* obj,
      const AsyncFactoryWorker::ExecutionProgress& progress)
     : m_obj(obj)
     , m_progress(progress) {
      m_obj->m_parser.subscribeTables(this);
    }

    ~Watcher() {
      m_obj->m_parser.subscribeTables(NULL);
    }

    void updateTable(uint8_t tId, dvbtee::decode::Table *table) {
      m_progress.Construct(
        table->getTableid(),
        table->getDecoderName(),
        table->toJson()
      );
    }

   private:
    dvbteeParser* m_obj;
    const AsyncFactoryWorker::ExecutionProgress& m_progress;
  };
};

void dvbteeParser::feed(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  int lastArg = info.Length() - 1;

  if ((lastArg >= 1) && (info[lastArg]->IsFunction()) &&
      (info[lastArg - 1]->IsFunction())) {
    Nan::Callback *progress = new Nan::Callback(
      info[lastArg - 1].As<v8::Function>()
    );
    Nan::Callback *callback = new Nan::Callback(
      info[lastArg].As<v8::Function>()
    );
    Nan::AsyncQueueWorker(new FeedWorker(callback, progress, info));

  } else {
    info.GetReturnValue().Set(Nan::New(-1));
  }
}
<commit_msg>dvbtee-parser: clean up Krufky / Nan namespace<commit_after>/*********************************************************************
 * node-dvbtee for Node.js
 *
 * Copyright (c) 2017 Michael Ira Krufky <https://github.com/mkrufky>
 *
 * MIT License <https://github.com/mkrufky/node-dvbtee/blob/master/LICENSE>
 *
 * See https://github.com/mkrufky/node-dvbtee for more information
 ********************************************************************/

#include <nan.h>
#include "dvbtee-parser.h"  // NOLINT(build/include)

Nan::Persistent<v8::Function> dvbteeParser::constructor;

dvbteeParser::dvbteeParser() {
}

dvbteeParser::~dvbteeParser() {
}

void dvbteeParser::Init(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE exports) {
  Nan::HandleScope scope;

  v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
  tpl->SetClassName(Nan::New("Parser").ToLocalChecked());
  tpl->InstanceTemplate()->SetInternalFieldCount(1);

  Nan::SetPrototypeMethod(tpl, "reset",        reset);
  Nan::SetPrototypeMethod(tpl, "feed",         feed);
  Nan::SetPrototypeMethod(tpl, "enableEttCollection", enableEttCollection);

  constructor.Reset(tpl->GetFunction());
  exports->Set(Nan::New("Parser").ToLocalChecked(), tpl->GetFunction());
}

void dvbteeParser::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  if (info.IsConstructCall()) {
    // Invoked as constructor: `new dvbteeParser(...)`
    dvbteeParser* obj = new dvbteeParser();
    obj->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  } else {
    // Invoked as plain function `dvbteeParser(...)`, turn into construct call.
    const int argc = 0;
    v8::Local<v8::Value> argv[argc] = { };
    v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);
    info.GetReturnValue().Set(cons->NewInstance(argc, argv));
  }
}

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

void dvbteeParser::enableEttCollection(
  const Nan::FunctionCallbackInfo<v8::Value>& info) {
  dvbteeParser* obj = ObjectWrap::Unwrap<dvbteeParser>(info.Holder());

  obj->m_parser.enable_ett_collection();

  info.GetReturnValue().Set(info.Holder());
}

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

class ResetWorker : public Nan::AsyncWorker {
 public:
  ResetWorker(Nan::Callback *callback,
              const Nan::FunctionCallbackInfo<v8::Value>& info)
    : Nan::AsyncWorker(callback) {
      m_obj = Nan::ObjectWrap::Unwrap<dvbteeParser>(info.Holder());
    }
  ~ResetWorker() {
    }

  void Execute () {
    m_obj->m_parser.cleanup();
    m_obj->m_parser.reset();
  }

  void HandleOKCallback () {
    Nan::HandleScope scope;

    v8::Local<v8::Value> argv[] = {
        Nan::Null()
      , Nan::New<v8::Number>(0)
    };

    callback->Call(2, argv);
  }

 private:
  dvbteeParser* m_obj;
};

void dvbteeParser::reset(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  // If a callback function is supplied, this method will run async
  // otherwise, it will run synchronous

  int lastArg = info.Length() - 1;

  if ((lastArg >= 0) && (info[lastArg]->IsFunction())) {
    Nan::Callback *callback = new Nan::Callback(
      info[lastArg].As<v8::Function>()
    );
    Nan::AsyncQueueWorker(new ResetWorker(callback, info));

  } else {
    dvbteeParser* obj = ObjectWrap::Unwrap<dvbteeParser>(info.Holder());

    obj->m_parser.cleanup();
    obj->m_parser.reset();

    info.GetReturnValue().Set(info.Holder());
  }
}

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

namespace Krufky {

namespace Nan {

using namespace ::Nan;

template<class T, typename... Targs>
/* abstract */ class AsyncBareFactoryWorker : public AsyncWorker {
 public:
  explicit AsyncBareFactoryWorker(Callback *callback_)
      : AsyncWorker(callback_) {
    async = new uv_async_t;
    uv_async_init(
        uv_default_loop()
      , async
      , AsyncProgress_
    );
    async->data = this;
  }

  virtual ~AsyncBareFactoryWorker() {
  }

  virtual void WorkProgress() = 0;

  class ExecutionProgress {
    friend class AsyncBareFactoryWorker;
   public:
    void Signal() const {
        uv_async_send(that_->async);
    }

    void Construct(Targs... Fargs) const {
        that_->ConstructProgress_(Fargs...);
    }

   private:
    explicit ExecutionProgress(AsyncBareFactoryWorker *that) : that_(that) {}
    NAN_DISALLOW_ASSIGN_COPY_MOVE(ExecutionProgress)
    AsyncBareFactoryWorker* const that_;
  };

  virtual void Execute(const ExecutionProgress& progress) = 0;
  virtual void HandleProgressCallback(const T *data, size_t size) = 0;

  virtual void Destroy() {
      uv_close(reinterpret_cast<uv_handle_t*>(async), AsyncClose_);
  }

 private:
  void Execute() /*final override*/ {
      ExecutionProgress progress(this);
      Execute(progress);
  }

  virtual void ConstructProgress_(Targs... Fargs) = 0;

  inline static NAUV_WORK_CB(AsyncProgress_) {
    AsyncBareFactoryWorker *worker =
            static_cast<AsyncBareFactoryWorker*>(async->data);
    worker->WorkProgress();
  }

  inline static void AsyncClose_(uv_handle_t* handle) {
    AsyncBareFactoryWorker *worker =
            static_cast<AsyncBareFactoryWorker*>(handle->data);
    delete reinterpret_cast<uv_async_t*>(handle);
    delete worker;
  }

 protected:
  uv_async_t *async;
};

template<class T, typename... Targs>
/* abstract */
class AsyncFactoryWorker : public AsyncBareFactoryWorker<T, Targs...> {
 public:
  explicit AsyncFactoryWorker(Callback *callback_)
      : AsyncBareFactoryWorker<T, Targs...>(callback_) {
    uv_mutex_init(&async_lock);
  }

  virtual ~AsyncFactoryWorker() {
    uv_mutex_lock(&async_lock);

    while (!asyncdata_.empty()) {
      T *data = asyncdata_.front();

      asyncdata_.pop();

      delete data;
    }

    uv_mutex_unlock(&async_lock);
    uv_mutex_destroy(&async_lock);
  }

  void WorkComplete() {
    WorkProgress();
    AsyncWorker::WorkComplete();
  }

  void WorkProgress() {
    uv_mutex_lock(&async_lock);

    while (!asyncdata_.empty()) {
      T *data = asyncdata_.front();
      asyncdata_.pop();
      uv_mutex_unlock(&async_lock);

      // Don't send progress events after we've already completed.
      if (this->callback) {
          this->HandleProgressCallback(data, 1);
      }

      delete data;

      uv_mutex_lock(&async_lock);
    }

    uv_mutex_unlock(&async_lock);
  }

 private:
  void ConstructProgress_(Targs... Fargs) {
    T *data = new T(Fargs...);

    uv_mutex_lock(&async_lock);
    asyncdata_.push(data);
    uv_mutex_unlock(&async_lock);

    uv_async_send(this->async);
  }

  uv_mutex_t async_lock;
  std::queue<T*> asyncdata_;
};

}  // namespace Nan

}  // namespace Krufky

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

class TableData {
 public:
  explicit TableData(
  const uint8_t& tableId,
  const std::string& decoderName,
  const std::string& json)
  : tableId(tableId)
  , decoderName(decoderName)
  , json(json) {
  }

  const uint8_t tableId;
  const std::string decoderName;
  const std::string json;

 private:
  TableData() = delete;
  TableData(const TableData&) = delete;
  TableData& operator= (const TableData&) = delete;
};

class FeedWorker: public Krufky::Nan::AsyncFactoryWorker<TableData, const uint8_t&, const std::string&, const std::string&> {
 public:
  FeedWorker(Nan::Callback *callback,
             Nan::Callback *progress,
             const Nan::FunctionCallbackInfo<v8::Value>& info)
    : AsyncFactoryWorker<TableData, const uint8_t&, const std::string&, const std::string&>(callback)
    , m_progress(progress)
    , m_buf(NULL)
    , m_buf_len(0)
    , m_ret(-1) {
      m_obj = Nan::ObjectWrap::Unwrap<dvbteeParser>(info.Holder());

      if ((info[0]->IsObject()) && (info[1]->IsNumber())) {
        const char *key = "buf";
        SaveToPersistent(key, info[0]);
        Nan::MaybeLocal<v8::Object> mbBuf =
          Nan::To<v8::Object>(GetFromPersistent(key));

        if (!mbBuf.IsEmpty()) {
          m_buf = node::Buffer::Data(mbBuf.ToLocalChecked());
          m_buf_len = info[1]->Uint32Value();
        }
      }
    }
  ~FeedWorker() {
    }

  void Execute(const AsyncFactoryWorker::ExecutionProgress& progress) {
    if ((m_buf) && (m_buf_len)) {
      Watcher watch(this->m_obj, progress);

      m_ret = m_obj->m_parser.feed(
        m_buf_len, reinterpret_cast<uint8_t*>(m_buf)
      );
    }
    if (m_ret < 0) {
      SetErrorMessage("invalid buffer / length");
    }
  }

  void HandleProgressCallback(const TableData *async_data, size_t count) {
    Nan::HandleScope scope;

    if (!m_progress->IsEmpty()) {
      for (unsigned int i = 0; i < count; i++) {
        const TableData *data = &async_data[i];

        Nan::MaybeLocal<v8::String> jsonStr = Nan::New(data->json);
        if (!jsonStr.IsEmpty()) {
          Nan::MaybeLocal<v8::Value> jsonVal =
            m_obj->NanJSON.Parse(jsonStr.ToLocalChecked());

          if (!jsonVal.IsEmpty()) {
            Nan::MaybeLocal<v8::String> decoderName =
              Nan::New(data->decoderName);

            v8::Local<v8::Value> decoderNameArg;
            if (decoderName.IsEmpty())
              decoderNameArg = Nan::Null();
            else
              decoderNameArg = decoderName.ToLocalChecked();

            v8::Local<v8::Value> argv[] = {
              Nan::New(data->tableId),
              decoderNameArg,
              jsonVal.ToLocalChecked()
            };

            m_progress->Call(3, argv);
          }
        }
      }
    }
  }

  void HandleOKCallback() {
    Nan::HandleScope scope;

    v8::Local<v8::Value> argv[] = {
        Nan::Null()
      , Nan::New<v8::Number>(m_ret)
    };

    callback->Call(2, argv);
  }

 private:
  Nan::Callback *m_progress;
  dvbteeParser* m_obj;
  char* m_buf;
  unsigned int m_buf_len;
  int m_ret;

  class Watcher: public dvbtee::decode::TableWatcher {
   public:
    explicit Watcher(dvbteeParser* obj,
      const AsyncFactoryWorker::ExecutionProgress& progress)
     : m_obj(obj)
     , m_progress(progress) {
      m_obj->m_parser.subscribeTables(this);
    }

    ~Watcher() {
      m_obj->m_parser.subscribeTables(NULL);
    }

    void updateTable(uint8_t tId, dvbtee::decode::Table *table) {
      m_progress.Construct(
        table->getTableid(),
        table->getDecoderName(),
        table->toJson()
      );
    }

   private:
    dvbteeParser* m_obj;
    const AsyncFactoryWorker::ExecutionProgress& m_progress;
  };
};

void dvbteeParser::feed(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  int lastArg = info.Length() - 1;

  if ((lastArg >= 1) && (info[lastArg]->IsFunction()) &&
      (info[lastArg - 1]->IsFunction())) {
    Nan::Callback *progress = new Nan::Callback(
      info[lastArg - 1].As<v8::Function>()
    );
    Nan::Callback *callback = new Nan::Callback(
      info[lastArg].As<v8::Function>()
    );
    Nan::AsyncQueueWorker(new FeedWorker(callback, progress, info));

  } else {
    info.GetReturnValue().Set(Nan::New(-1));
  }
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2014-2015 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include "catch.hpp"

#include "etl/etl.hpp"

//For some reasons, these tests fail to compile in template mode
//TODO: Fix that

TEST_CASE( "etl_traits/fast_vector_1", "etl_traits<fast_vector>" ) {
    using type = etl::fast_vector<double, 4>;
    type test_vector(3.3);

    REQUIRE(etl::etl_traits<type>::size(test_vector) == 4);
    REQUIRE(etl::etl_traits<type>::dimensions() == 1);
    REQUIRE(etl::size(test_vector) == 4);
    REQUIRE(etl::etl_traits<type>::is_value);
    REQUIRE(etl::etl_traits<type>::is_fast);

    constexpr const auto size_1 = etl::etl_traits<type>::size();
    REQUIRE(size_1 == 4);

    constexpr const auto size_2 = etl::size(test_vector);
    REQUIRE(size_2 == 4);

    constexpr const auto size_3 = etl::etl_traits<type>::dim<0>();
    REQUIRE(size_3 == 4);

    constexpr const auto size_4 = etl::etl_traits<type>::dimensions();
    REQUIRE(size_4 == 1);

    constexpr const auto size_5 = etl::dimensions(test_vector);
    REQUIRE(size_5 == 1);
}

TEST_CASE( "etl_traits/fast_matrix_1", "etl_traits<fast_matrix>" ) {
    using type = etl::fast_matrix<double, 3, 2>;
    type test_matrix(3.3);

    REQUIRE(etl::etl_traits<type>::size(test_matrix) == 6);
    REQUIRE(etl::size(test_matrix) == 6);
    REQUIRE(etl::rows(test_matrix) == 3);
    REQUIRE(etl::columns(test_matrix) == 2);
    REQUIRE(etl::etl_traits<type>::dimensions() == 2);
    REQUIRE(etl::dimensions(test_matrix) == 2);
    REQUIRE(etl::etl_traits<type>::is_value);
    REQUIRE(etl::etl_traits<type>::is_fast);

    constexpr const auto size_1 = etl::etl_traits<type>::size();
    constexpr const auto dim_1 = etl::etl_traits<type>::dimensions();

    REQUIRE(size_1 == 6);
    REQUIRE(dim_1 == 2);

    constexpr const auto size_2 = etl::size(test_matrix);
    constexpr const auto rows_2 = etl::rows(test_matrix);
    constexpr const auto columns_2 = etl::columns(test_matrix);
    constexpr const auto dim_2 = etl::dimensions(test_matrix);

    REQUIRE(size_2 == 6);
    REQUIRE(rows_2 == 3);
    REQUIRE(columns_2 == 2);
    REQUIRE(dim_2 == 2);

    constexpr const auto rows_3 = etl::etl_traits<type>::dim<0>();
    constexpr const auto columns_3 = etl::etl_traits<type>::dim<1>();

    REQUIRE(rows_3 == 3);
    REQUIRE(columns_3 == 2);
}

TEST_CASE( "etl_traits/fast_matrix_2", "etl_traits<fast_matrix>" ) {
    using type = etl::fast_matrix<double, 3, 2, 4, 1>;
    type test_matrix(3.3);

    REQUIRE(etl::etl_traits<type>::size(test_matrix) == 24);
    REQUIRE(etl::size(test_matrix) == 24);
    REQUIRE(etl::rows(test_matrix) == 3);
    REQUIRE(etl::columns(test_matrix) == 2);
    REQUIRE(etl::etl_traits<type>::dimensions() == 4);
    REQUIRE(etl::dimensions(test_matrix) == 4);
    REQUIRE(etl::etl_traits<type>::is_value);
    REQUIRE(etl::etl_traits<type>::is_fast);

    constexpr const auto size_1 = etl::etl_traits<type>::size();
    constexpr const auto dim_1 = etl::etl_traits<type>::dimensions();

    REQUIRE(size_1 == 24);
    REQUIRE(dim_1 == 4);

    constexpr const auto size_2 = etl::size(test_matrix);
    constexpr const auto rows_2 = etl::rows(test_matrix);
    constexpr const auto columns_2 = etl::columns(test_matrix);
    constexpr const auto dim_2 = etl::dimensions(test_matrix);

    REQUIRE(size_2 == 24);
    REQUIRE(rows_2 == 3);
    REQUIRE(columns_2 == 2);
    REQUIRE(dim_2 == 4);

    constexpr const auto rows_3 = etl::etl_traits<type>::dim<0>();
    constexpr const auto columns_3 = etl::etl_traits<type>::dim<1>();
    constexpr const auto dim_2_3 = etl::etl_traits<type>::dim<2>();
    constexpr const auto dim_3_3 = etl::etl_traits<type>::dim<3>();

    REQUIRE(rows_3 == 3);
    REQUIRE(columns_3 == 2);
    REQUIRE(dim_2_3 == 4);
    REQUIRE(dim_3_3 == 1);
}

TEST_CASE( "etl_traits/dyn_vector_1", "etl_traits<dyn_vector>" ) {
    using type = etl::dyn_vector<double>;
    type test_vector(4, 3.3);

    REQUIRE(etl::etl_traits<type>::size(test_vector) == 4);
    REQUIRE(etl::size(test_vector) == 4);
    REQUIRE(etl::etl_traits<type>::dim(test_vector, 0) == 4);
    REQUIRE(etl::etl_traits<type>::dimensions() == 1);
    REQUIRE(etl::dimensions(test_vector) == 1);
    REQUIRE(etl::etl_traits<type>::is_value);
    REQUIRE(!etl::etl_traits<type>::is_fast);
}

TEST_CASE( "etl_traits/dyn_matrix_1", "etl_traits<dyn_matrix>" ) {
    using type = etl::dyn_matrix<double>;
    type test_matrix(3, 2, 3.3);

    REQUIRE(etl::etl_traits<type>::size(test_matrix) == 6);
    REQUIRE(etl::size(test_matrix) == 6);
    REQUIRE(etl::rows(test_matrix) == 3);
    REQUIRE(etl::columns(test_matrix) == 2);
    REQUIRE(etl::etl_traits<type>::dimensions() == 2);
    REQUIRE(etl::dimensions(test_matrix) == 2);
    REQUIRE(etl::etl_traits<type>::dim(test_matrix, 0) == 3);
    REQUIRE(etl::etl_traits<type>::dim(test_matrix, 1) == 2);
    REQUIRE(etl::etl_traits<type>::is_value);
    REQUIRE(!etl::etl_traits<type>::is_fast);
}

TEST_CASE( "etl_traits/unary_dyn_mat", "etl_traits<unary<dyn_mat>>" ) {
    using mat_type = etl::dyn_matrix<double>;
    mat_type test_matrix(3, 2, 3.3);

    using expr_type = decltype(log(test_matrix));
    expr_type expr(test_matrix);

    REQUIRE(etl::etl_traits<expr_type>::size(expr) == 6);
    REQUIRE(etl::size(expr) == 6);
    REQUIRE(etl::rows(expr) == 3);
    REQUIRE(etl::columns(expr) == 2);
    REQUIRE(etl::etl_traits<expr_type>::dim(expr, 0) == 3);
    REQUIRE(etl::etl_traits<expr_type>::dim(expr, 1) == 2);
    REQUIRE(etl::etl_traits<expr_type>::dimensions() == 2);
    REQUIRE(etl::dimensions(expr) == 2);
    REQUIRE(!etl::etl_traits<expr_type>::is_value);
    REQUIRE(!etl::etl_traits<expr_type>::is_fast);
}

TEST_CASE( "etl_traits/binary_dyn_mat", "etl_traits<binary<dyn_mat, dyn_mat>>" ) {
    using mat_type = etl::dyn_matrix<double>;
    mat_type test_matrix(3, 2, 3.3);

    using expr_type = decltype(test_matrix + test_matrix);
    expr_type expr(test_matrix, test_matrix);

    REQUIRE(etl::etl_traits<expr_type>::size(expr) == 6);
    REQUIRE(etl::size(expr) == 6);
    REQUIRE(etl::rows(expr) == 3);
    REQUIRE(etl::columns(expr) == 2);
    REQUIRE(etl::etl_traits<expr_type>::dimensions() == 2);
    REQUIRE(etl::dimensions(expr) == 2);
    REQUIRE(etl::etl_traits<expr_type>::dim(expr, 0) == 3);
    REQUIRE(etl::etl_traits<expr_type>::dim(expr, 1) == 2);
    REQUIRE(!etl::etl_traits<expr_type>::is_value);
    REQUIRE(!etl::etl_traits<expr_type>::is_fast);
}

TEST_CASE( "etl_traits/unary_fast_mat", "etl_traits<unary<fast_mat>>" ) {
    using mat_type = etl::fast_matrix<double, 3, 2>;
    mat_type test_matrix(3.3);

    using expr_type = decltype(log(test_matrix));
    expr_type expr(test_matrix);

    REQUIRE(etl::etl_traits<expr_type>::size(expr) == 6);
    REQUIRE(etl::size(expr) == 6);
    REQUIRE(etl::rows(expr) == 3);
    REQUIRE(etl::columns(expr) == 2);
    REQUIRE(etl::etl_traits<expr_type>::dimensions() == 2);
    REQUIRE(etl::dimensions(expr) == 2);
    REQUIRE(!etl::etl_traits<expr_type>::is_value);
    REQUIRE(etl::etl_traits<expr_type>::is_fast);

    constexpr const auto size_1 = etl::etl_traits<expr_type>::size();
    constexpr const auto dim_1 = etl::etl_traits<expr_type>::dimensions();

    REQUIRE(size_1 == 6);
    REQUIRE(dim_1 == 2);

    constexpr const auto size_2 = etl::size(expr);
    constexpr const auto rows_2 = etl::rows(expr);
    constexpr const auto columns_2 = etl::columns(expr);
    constexpr const auto dim_2 = etl::dimensions(expr);

    REQUIRE(size_2 == 6);
    REQUIRE(rows_2 == 3);
    REQUIRE(columns_2 == 2);
    REQUIRE(dim_2 == 2);

    constexpr const auto rows_3 = etl::etl_traits<expr_type>::dim<0>();
    constexpr const auto columns_3 = etl::etl_traits<expr_type>::dim<1>();

    REQUIRE(rows_3 == 3);
    REQUIRE(columns_3 == 2);
}

TEST_CASE( "etl_traits/binary_fast_mat", "etl_traits<binary<fast_mat, fast_mat>>" ) {
    using mat_type = etl::fast_matrix<double, 3, 2>;
    mat_type test_matrix(3.3);

    using expr_type = decltype(test_matrix + test_matrix);
    expr_type expr(test_matrix, test_matrix);

    REQUIRE(etl::etl_traits<expr_type>::size(expr) == 6);
    REQUIRE(etl::size(expr) == 6);
    REQUIRE(etl::rows(expr) == 3);
    REQUIRE(etl::columns(expr) == 2);
    REQUIRE(etl::etl_traits<expr_type>::dimensions() == 2);
    REQUIRE(etl::dimensions(expr) == 2);
    REQUIRE(!etl::etl_traits<expr_type>::is_value);
    REQUIRE(etl::etl_traits<expr_type>::is_fast);

    constexpr const auto size_1 = etl::etl_traits<expr_type>::size();
    constexpr const auto dim_1 = etl::etl_traits<expr_type>::dimensions();

    REQUIRE(size_1 == 6);
    REQUIRE(dim_1 == 2);

    constexpr const auto size_2 = etl::size(expr);
    constexpr const auto rows_2 = etl::rows(expr);
    constexpr const auto columns_2 = etl::columns(expr);
    constexpr const auto dim_2 = etl::dimensions(expr);

    REQUIRE(size_2 == 6);
    REQUIRE(rows_2 == 3);
    REQUIRE(columns_2 == 2);
    REQUIRE(dim_2 == 2);

    constexpr const auto rows_3 = etl::etl_traits<expr_type>::dim<0>();
    constexpr const auto columns_3 = etl::etl_traits<expr_type>::dim<1>();

    REQUIRE(rows_3 == 3);
    REQUIRE(columns_3 == 2);
}
<commit_msg>Transform into template test<commit_after>//=======================================================================
// Copyright (c) 2014-2015 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include "catch.hpp"
#include "template_test.hpp"

#include "etl/etl.hpp"

TEMPLATE_TEST_CASE_2( "etl_traits/fast_vector_1", "etl_traits<fast_vector>", ZZZ, double, float ) {
    using type = etl::fast_vector<ZZZ, 4>;
    type test_vector(3.3);

    REQUIRE(etl::etl_traits<type>::size(test_vector) == 4);
    REQUIRE(etl::etl_traits<type>::dimensions() == 1);
    REQUIRE(etl::size(test_vector) == 4);
    REQUIRE(etl::etl_traits<type>::is_value);
    REQUIRE(etl::etl_traits<type>::is_fast);

    constexpr const auto size_1 = etl::etl_traits<type>::size();
    REQUIRE(size_1 == 4);

    constexpr const auto size_2 = etl::size(test_vector);
    REQUIRE(size_2 == 4);

    constexpr const auto size_3 = etl::etl_traits<type>::template dim<0>();
    REQUIRE(size_3 == 4);

    constexpr const auto size_4 = etl::etl_traits<type>::dimensions();
    REQUIRE(size_4 == 1);

    constexpr const auto size_5 = etl::dimensions(test_vector);
    REQUIRE(size_5 == 1);
}

TEMPLATE_TEST_CASE_2( "etl_traits/fast_matrix_1", "etl_traits<fast_matrix>", Z, float, double ) {
    using type = etl::fast_matrix<Z, 3, 2>;
    type test_matrix(3.3);

    REQUIRE(etl::etl_traits<type>::size(test_matrix) == 6);
    REQUIRE(etl::size(test_matrix) == 6);
    REQUIRE(etl::rows(test_matrix) == 3);
    REQUIRE(etl::columns(test_matrix) == 2);
    REQUIRE(etl::etl_traits<type>::dimensions() == 2);
    REQUIRE(etl::dimensions(test_matrix) == 2);
    REQUIRE(etl::etl_traits<type>::is_value);
    REQUIRE(etl::etl_traits<type>::is_fast);

    constexpr const auto size_1 = etl::etl_traits<type>::size();
    constexpr const auto dim_1 = etl::etl_traits<type>::dimensions();

    REQUIRE(size_1 == 6);
    REQUIRE(dim_1 == 2);

    constexpr const auto size_2 = etl::size(test_matrix);
    constexpr const auto rows_2 = etl::rows(test_matrix);
    constexpr const auto columns_2 = etl::columns(test_matrix);
    constexpr const auto dim_2 = etl::dimensions(test_matrix);

    REQUIRE(size_2 == 6);
    REQUIRE(rows_2 == 3);
    REQUIRE(columns_2 == 2);
    REQUIRE(dim_2 == 2);

    constexpr const auto rows_3 = etl::etl_traits<type>::template dim<0>();
    constexpr const auto columns_3 = etl::etl_traits<type>::template dim<1>();

    REQUIRE(rows_3 == 3);
    REQUIRE(columns_3 == 2);
}

TEMPLATE_TEST_CASE_2( "etl_traits/fast_matrix_2", "etl_traits<fast_matrix>", Z, float, double ) {
    using type = etl::fast_matrix<Z, 3, 2, 4, 1>;
    type test_matrix(3.3);

    REQUIRE(etl::etl_traits<type>::size(test_matrix) == 24);
    REQUIRE(etl::size(test_matrix) == 24);
    REQUIRE(etl::rows(test_matrix) == 3);
    REQUIRE(etl::columns(test_matrix) == 2);
    REQUIRE(etl::etl_traits<type>::dimensions() == 4);
    REQUIRE(etl::dimensions(test_matrix) == 4);
    REQUIRE(etl::etl_traits<type>::is_value);
    REQUIRE(etl::etl_traits<type>::is_fast);

    constexpr const auto size_1 = etl::etl_traits<type>::size();
    constexpr const auto dim_1 = etl::etl_traits<type>::dimensions();

    REQUIRE(size_1 == 24);
    REQUIRE(dim_1 == 4);

    constexpr const auto size_2 = etl::size(test_matrix);
    constexpr const auto rows_2 = etl::rows(test_matrix);
    constexpr const auto columns_2 = etl::columns(test_matrix);
    constexpr const auto dim_2 = etl::dimensions(test_matrix);

    REQUIRE(size_2 == 24);
    REQUIRE(rows_2 == 3);
    REQUIRE(columns_2 == 2);
    REQUIRE(dim_2 == 4);

    constexpr const auto rows_3 = etl::etl_traits<type>::template dim<0>();
    constexpr const auto columns_3 = etl::etl_traits<type>::template dim<1>();
    constexpr const auto dim_2_3 = etl::etl_traits<type>::template dim<2>();
    constexpr const auto dim_3_3 = etl::etl_traits<type>::template dim<3>();

    REQUIRE(rows_3 == 3);
    REQUIRE(columns_3 == 2);
    REQUIRE(dim_2_3 == 4);
    REQUIRE(dim_3_3 == 1);
}

TEMPLATE_TEST_CASE_2( "etl_traits/dyn_vector_1", "etl_traits<dyn_vector>", Z, float, double ) {
    using type = etl::dyn_vector<Z>;
    type test_vector(4, 3.3);

    REQUIRE(etl::etl_traits<type>::size(test_vector) == 4);
    REQUIRE(etl::size(test_vector) == 4);
    REQUIRE(etl::etl_traits<type>::dim(test_vector, 0) == 4);
    REQUIRE(etl::etl_traits<type>::dimensions() == 1);
    REQUIRE(etl::dimensions(test_vector) == 1);
    REQUIRE(etl::etl_traits<type>::is_value);
    REQUIRE(!etl::etl_traits<type>::is_fast);
}

TEMPLATE_TEST_CASE_2( "etl_traits/dyn_matrix_1", "etl_traits<dyn_matrix>", Z, float, double ) {
    using type = etl::dyn_matrix<Z>;
    type test_matrix(3, 2, 3.3);

    REQUIRE(etl::etl_traits<type>::size(test_matrix) == 6);
    REQUIRE(etl::size(test_matrix) == 6);
    REQUIRE(etl::rows(test_matrix) == 3);
    REQUIRE(etl::columns(test_matrix) == 2);
    REQUIRE(etl::etl_traits<type>::dimensions() == 2);
    REQUIRE(etl::dimensions(test_matrix) == 2);
    REQUIRE(etl::etl_traits<type>::dim(test_matrix, 0) == 3);
    REQUIRE(etl::etl_traits<type>::dim(test_matrix, 1) == 2);
    REQUIRE(etl::etl_traits<type>::is_value);
    REQUIRE(!etl::etl_traits<type>::is_fast);
}

TEMPLATE_TEST_CASE_2( "etl_traits/unary_dyn_mat", "etl_traits<unary<dyn_mat>>", Z, float, double ) {
    using mat_type = etl::dyn_matrix<Z>;
    mat_type test_matrix(3, 2, 3.3);

    using expr_type = decltype(log(test_matrix));
    expr_type expr(test_matrix);

    REQUIRE(etl::etl_traits<expr_type>::size(expr) == 6);
    REQUIRE(etl::size(expr) == 6);
    REQUIRE(etl::rows(expr) == 3);
    REQUIRE(etl::columns(expr) == 2);
    REQUIRE(etl::etl_traits<expr_type>::dim(expr, 0) == 3);
    REQUIRE(etl::etl_traits<expr_type>::dim(expr, 1) == 2);
    REQUIRE(etl::etl_traits<expr_type>::dimensions() == 2);
    REQUIRE(etl::dimensions(expr) == 2);
    REQUIRE(!etl::etl_traits<expr_type>::is_value);
    REQUIRE(!etl::etl_traits<expr_type>::is_fast);
}

TEMPLATE_TEST_CASE_2( "etl_traits/binary_dyn_mat", "etl_traits<binary<dyn_mat, dyn_mat>>", Z, float, double ) {
    using mat_type = etl::dyn_matrix<Z>;
    mat_type test_matrix(3, 2, 3.3);

    using expr_type = decltype(test_matrix + test_matrix);
    expr_type expr(test_matrix, test_matrix);

    REQUIRE(etl::etl_traits<expr_type>::size(expr) == 6);
    REQUIRE(etl::size(expr) == 6);
    REQUIRE(etl::rows(expr) == 3);
    REQUIRE(etl::columns(expr) == 2);
    REQUIRE(etl::etl_traits<expr_type>::dimensions() == 2);
    REQUIRE(etl::dimensions(expr) == 2);
    REQUIRE(etl::etl_traits<expr_type>::dim(expr, 0) == 3);
    REQUIRE(etl::etl_traits<expr_type>::dim(expr, 1) == 2);
    REQUIRE(!etl::etl_traits<expr_type>::is_value);
    REQUIRE(!etl::etl_traits<expr_type>::is_fast);
}

TEMPLATE_TEST_CASE_2( "etl_traits/unary_fast_mat", "etl_traits<unary<fast_mat>>", Z, float, double ) {
    using mat_type = etl::fast_matrix<Z, 3, 2>;
    mat_type test_matrix(3.3);

    using expr_type = decltype(log(test_matrix));
    expr_type expr(test_matrix);

    REQUIRE(etl::etl_traits<expr_type>::size(expr) == 6);
    REQUIRE(etl::size(expr) == 6);
    REQUIRE(etl::rows(expr) == 3);
    REQUIRE(etl::columns(expr) == 2);
    REQUIRE(etl::etl_traits<expr_type>::dimensions() == 2);
    REQUIRE(etl::dimensions(expr) == 2);
    REQUIRE(!etl::etl_traits<expr_type>::is_value);
    REQUIRE(etl::etl_traits<expr_type>::is_fast);

    constexpr const auto size_1 = etl::etl_traits<expr_type>::size();
    constexpr const auto dim_1 = etl::etl_traits<expr_type>::dimensions();

    REQUIRE(size_1 == 6);
    REQUIRE(dim_1 == 2);

    constexpr const auto size_2 = etl::size(expr);
    constexpr const auto rows_2 = etl::rows(expr);
    constexpr const auto columns_2 = etl::columns(expr);
    constexpr const auto dim_2 = etl::dimensions(expr);

    REQUIRE(size_2 == 6);
    REQUIRE(rows_2 == 3);
    REQUIRE(columns_2 == 2);
    REQUIRE(dim_2 == 2);

    constexpr const auto rows_3 = etl::etl_traits<expr_type>::template dim<0>();
    constexpr const auto columns_3 = etl::etl_traits<expr_type>::template dim<1>();

    REQUIRE(rows_3 == 3);
    REQUIRE(columns_3 == 2);
}

TEMPLATE_TEST_CASE_2( "etl_traits/binary_fast_mat", "etl_traits<binary<fast_mat, fast_mat>>", Z, float, double ) {
    using mat_type = etl::fast_matrix<Z, 3, 2>;
    mat_type test_matrix(3.3);

    using expr_type = decltype(test_matrix + test_matrix);
    expr_type expr(test_matrix, test_matrix);

    REQUIRE(etl::etl_traits<expr_type>::size(expr) == 6);
    REQUIRE(etl::size(expr) == 6);
    REQUIRE(etl::rows(expr) == 3);
    REQUIRE(etl::columns(expr) == 2);
    REQUIRE(etl::etl_traits<expr_type>::dimensions() == 2);
    REQUIRE(etl::dimensions(expr) == 2);
    REQUIRE(!etl::etl_traits<expr_type>::is_value);
    REQUIRE(etl::etl_traits<expr_type>::is_fast);

    constexpr const auto size_1 = etl::etl_traits<expr_type>::size();
    constexpr const auto dim_1 = etl::etl_traits<expr_type>::dimensions();

    REQUIRE(size_1 == 6);
    REQUIRE(dim_1 == 2);

    constexpr const auto size_2 = etl::size(expr);
    constexpr const auto rows_2 = etl::rows(expr);
    constexpr const auto columns_2 = etl::columns(expr);
    constexpr const auto dim_2 = etl::dimensions(expr);

    REQUIRE(size_2 == 6);
    REQUIRE(rows_2 == 3);
    REQUIRE(columns_2 == 2);
    REQUIRE(dim_2 == 2);

    constexpr const auto rows_3 = etl::etl_traits<expr_type>::template dim<0>();
    constexpr const auto columns_3 = etl::etl_traits<expr_type>::template dim<1>();

    REQUIRE(rows_3 == 3);
    REQUIRE(columns_3 == 2);
}
<|endoftext|>