<commit_before>/*
 * =====================================================================================
 *
 *       Filename:  InputParser.cpp
 *
 *    Description:
 *
 *        Version:  1.0
 *        Created:  06/25/2017 01:13:39
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Baoxing Song (songbx.me), songbaoxing168@163.com
 *
 * =====================================================================================
 */
#include <stdlib.h>
/*************************************************************************

 ************************************************************************/

#include <iostream>
#include "InputParser.h"

InputParser::InputParser (int &argc, char **argv){
    for (int i=1; i < argc; ++i)
        this->tokens.push_back(std::string(argv[i]));
}

const std::string InputParser::getCmdOption( std::string &option) {
    std::vector<std::string>::iterator itr;
    itr =  std::find(this->tokens.begin(), this->tokens.end(), option);
    if (itr != this->tokens.end() && ++itr != this->tokens.end()){
        return *itr;
    }
    return "";
}

std::string InputParser::getCmdOption( const char* o) {
    std::string option = o;
    return getCmdOption(option);
}

bool InputParser::cmdOptionExists(std::string &option) {
    return std::find(this->tokens.begin(), this->tokens.end(), option)
           != this->tokens.end();
}

bool InputParser::cmdOptionExists( const char* o){
    std::string option = o;
    return cmdOptionExists(option);
}

void usage( ){
    std::string progName = "gean";
    std::cout << "Program " << progName << std::endl <<
    "Usage: "<<progName<<" <command> [options]"<< std::endl <<
    "Commands:"<< std::endl <<
        " -- variant calling:" << std::endl <<
        "    pseudogeno  create pseudo genome sequence" << std::endl <<
        "    lift        transform coordinate to another accession" << std::endl <<
        "    revlift     transform coordinate of another accession to reference" << std::endl <<
        "    liftgff     transform all the GFF/GTF coordinates" << std::endl <<
        "    revliftgff  transform all the GFF/GTF coordinates back to reference" << std::endl <<
        "    reanva      update variants records for functional annotation" << std::endl <<
        "    gff2seq     get the protein/CDS/gene sequence of GFF/GTF file" << std::endl<<
        "    annowgr     annotate re-sequenced genome" << std::endl <<
        "    randomVar   assign a random position for each variant" << std::endl << std::endl <<
//        "annotationLiftOver             transform the reference GFF/GTF coordinate to the coordinate of another accession," << std::endl <<
//        "                               complementing with trying to keep the ORF/splice sites and complete as possible by " << std::endl <<
//        "                               genome sequence alignment" << std::endl <<
//        "annotationLiftOverAndOrth      transform the reference GFF/GTF coordinate to the coordinate of another accession," << std::endl <<
//        "                               complementing with trying to keep the ORF/splice sites and complete as possible by " << std::endl <<
//        "                               genome sequence alignment and then complementing with aligning CDS sequence of " << std::endl <<
//        "                               reference to the genome sequence of target line and then complementing with " << std::endl <<
//        "                               aligning protein sequence of reference to the genome sequence of target line" << std::endl <<
        " -- whole genome wide MSA:" << std::endl <<
        "    premsa      cut the whole genome sequence into fragments" << std::endl <<
        "    msatosdi    generate sdi files from MSA results" <<  std::endl << std::endl <<
        " -- de novo assembly genome:" << std::endl <<
        "    transgff    trans reference gff/gtf to de novo assembly genome" <<  std::endl <<
        "    spltogff    trans reference gff/gtf to de novo assembly genome using splice alignment sam file" <<  std::endl <<
        "    purifygff   purify the result from transgff" <<  std::endl <<
        "    sinsyn      (beta version) purify by keeping syntenic genes priorly and single copy genes (for inner species) " <<  std::endl <<
        "    sinsyn2     (beta version) purify by keeping syntenic genes priorly and single copy genes (for inter species) " <<  std::endl <<
        "    quotasyn    (beta version) quota syntenic blocks  " <<  std::endl <<
        "    orf         keep only ORF conserved genes " <<  std::endl <<
        "    varcall     variant calling for de novo genome sequence" <<  std::endl;
}
<commit_msg>disable not well tested syntenic functions<commit_after>/*
 * =====================================================================================
 *
 *       Filename:  InputParser.cpp
 *
 *    Description:
 *
 *        Version:  1.0
 *        Created:  06/25/2017 01:13:39
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Baoxing Song (songbx.me), songbaoxing168@163.com
 *
 * =====================================================================================
 */
#include <stdlib.h>
/*************************************************************************

 ************************************************************************/

#include <iostream>
#include "InputParser.h"

InputParser::InputParser (int &argc, char **argv){
    for (int i=1; i < argc; ++i)
        this->tokens.push_back(std::string(argv[i]));
}

const std::string InputParser::getCmdOption( std::string &option) {
    std::vector<std::string>::iterator itr;
    itr =  std::find(this->tokens.begin(), this->tokens.end(), option);
    if (itr != this->tokens.end() && ++itr != this->tokens.end()){
        return *itr;
    }
    return "";
}

std::string InputParser::getCmdOption( const char* o) {
    std::string option = o;
    return getCmdOption(option);
}

bool InputParser::cmdOptionExists(std::string &option) {
    return std::find(this->tokens.begin(), this->tokens.end(), option)
           != this->tokens.end();
}

bool InputParser::cmdOptionExists( const char* o){
    std::string option = o;
    return cmdOptionExists(option);
}

void usage( ){
    std::string progName = "gean";
    std::cout << "Program " << progName << std::endl <<
    "Usage: "<<progName<<" <command> [options]"<< std::endl <<
    "Commands:"<< std::endl <<
        " -- variant calling:" << std::endl <<
        "    pseudogeno  create pseudo genome sequence" << std::endl <<
        "    lift        transform coordinate to another accession" << std::endl <<
        "    revlift     transform coordinate of another accession to reference" << std::endl <<
        "    liftgff     transform all the GFF/GTF coordinates" << std::endl <<
        "    revliftgff  transform all the GFF/GTF coordinates back to reference" << std::endl <<
        "    reanva      update variants records for functional annotation" << std::endl <<
        "    gff2seq     get the protein/CDS/gene sequence of GFF/GTF file" << std::endl<<
        "    annowgr     annotate re-sequenced genome" << std::endl <<
        "    randomVar   assign a random position for each variant" << std::endl << std::endl <<
//        "annotationLiftOver             transform the reference GFF/GTF coordinate to the coordinate of another accession," << std::endl <<
//        "                               complementing with trying to keep the ORF/splice sites and complete as possible by " << std::endl <<
//        "                               genome sequence alignment" << std::endl <<
//        "annotationLiftOverAndOrth      transform the reference GFF/GTF coordinate to the coordinate of another accession," << std::endl <<
//        "                               complementing with trying to keep the ORF/splice sites and complete as possible by " << std::endl <<
//        "                               genome sequence alignment and then complementing with aligning CDS sequence of " << std::endl <<
//        "                               reference to the genome sequence of target line and then complementing with " << std::endl <<
//        "                               aligning protein sequence of reference to the genome sequence of target line" << std::endl <<
        " -- whole genome wide MSA:" << std::endl <<
        "    premsa      cut the whole genome sequence into fragments" << std::endl <<
        "    msatosdi    generate sdi files from MSA results" <<  std::endl << std::endl <<
        " -- de novo assembly genome:" << std::endl <<
        "    transgff    trans reference gff/gtf to de novo assembly genome" <<  std::endl <<
        "    spltogff    trans reference gff/gtf to de novo assembly genome using splice alignment sam file" <<  std::endl <<
        "    purifygff   purify the result from transgff" <<  std::endl <<
        "    sinsyn      (beta version) purify by keeping syntenic genes priorly and single copy genes (for inner species) " <<  std::endl <<
        //"    sinsyn2     (beta version) purify by keeping syntenic genes priorly and single copy genes (for inter species) " <<  std::endl <<
        //"    quotasyn    (beta version) quota syntenic blocks  " <<  std::endl <<
        "    orf         keep only ORF conserved genes " <<  std::endl <<
        "    varcall     variant calling for de novo genome sequence " <<  std::endl;
}
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "bmp.h"

#include <fstream>
#include <cstring>

namespace
{

// These structures are not in bmp.h as they will not be exposed outside this file.
// Structure definitions taken from http://en.wikipedia.org/wiki/BMP_file_format

#pragma pack(1)	// Force 1-byte alignment.
struct BitmapFileHeader
{
	std::uint16_t	Magic;
	std::uint32_t	Filesize;
	std::uint16_t	Reserved1;
	std::uint16_t	Reserved2;
	std::uint32_t	PixelArrayOffset;
};

#pragma pack(1)	// Force 1-byte alignment.
struct BitmapInfoHeader
{
	std::uint32_t	HeaderSize;
	std::int32_t	Width;
	std::int32_t	Height;
	std::uint16_t	NumColorPlanes;
	std::uint16_t	NumBitsPerPixel;
	std::uint32_t	CompressionMethod;
	std::uint32_t	PixelDataSize;
	std::int32_t	HorizontalResolution;
	std::int32_t	VerticalResolution;
	std::uint32_t	NumPaletteColors;
	std::uint32_t	NumImportantColors;
};

}

namespace BMP
{

// The first two bytes of a BMP file are always 0x42 and 0x4D.
// 0x4D42 is the 16-bit little endian representation.
static const uint16_t BMP_MAGIC = 0x4D42;

// Only 24-bit images are supported.
static const uint32_t BYTES_PER_PIXEL = 3;

/**
Validates the bitmap file header.

@param[in] Header	The bitmap file header to be validated.

@return True if the file header is valid and the image can be loaded, otherwise false.
*/
static bool ValidateFileHeader(const BitmapFileHeader& Header)
{
	// TODO: Better validation.
	return (Header.Magic == BMP_MAGIC);
}

/**
Validates the bitmap info header.

This function checks if the following requirements are met:
\li Header size is 40 bytes
\li Image width and height are positive
\li Number of color planes is 1
\li Number of bits per pixel is 242
\li The image is uncompressed.

@param[in] Header	The bitmap info header to be validated.

@return True if the requirements are met and the image can be loaded, otherwise false.
*/
static bool ValidateInfoHeader(const BitmapInfoHeader& Header)
{
	return (Header.HeaderSize == sizeof(BitmapInfoHeader))
		&& (Header.Width > 0)				// Width and height can be negative, but
		&& (Header.Height > 0)				// we don't support those either.
		&& (Header.NumColorPlanes == 1)		// 1 is the only valid value according to Wikipedia.
		&& (Header.NumBitsPerPixel == 24)	// Only 24-bit images are supported.
		&& (Header.CompressionMethod == 0); // Nonzero means compression is used.
}

/**
Loads and checks both bitmap file and info headers.

@param[in] FileStream	The input file stream.
@param[out] FileHeader	The bitmap file header read from the file.
@param[out] InfoHeader	The bitmap info header read from the file.

@return One of #BitmapLoadStatus codes.
*/
static BitmapLoadStatus LoadAndValidateHeaders(std::ifstream& FileStream, BitmapFileHeader* FileHeader,
											   BitmapInfoHeader* InfoHeader)
{
	FileStream.read(reinterpret_cast<char*>(FileHeader), sizeof(*FileHeader));
	if (FileStream.bad()) {
		return INVALID_FILE_HEADER;
	}

	if (!ValidateFileHeader(*FileHeader)) {
		return INVALID_FILE_HEADER;
	}

	FileStream.read(reinterpret_cast<char*>(InfoHeader), sizeof(*InfoHeader));
	if (FileStream.bad() || !ValidateInfoHeader(*InfoHeader)) {
		return INVALID_INFO_HEADER;
	}

	return LOAD_SUCCESS;
}

/**
Reads the pixel rows from the bitmap file.

@param[in] FileStream	The input file stream.
@param[out] DestBuffer	The buffer to write the pixels to.
@param[in] FileHeader	The bitmap file header.
@param[in] InfoHeader	The bitmap info header.

@return One of #BitmapLoadStatus codes.
*/
static BitmapLoadStatus LoadPixels(std::ifstream& FileStream, std::vector<std::uint8_t>* DestBuffer,
								   const BitmapFileHeader& FileHeader, const BitmapInfoHeader& InfoHeader)
{
	FileStream.seekg(FileHeader.PixelArrayOffset);

	DestBuffer->resize(InfoHeader.PixelDataSize);
	auto buffer = reinterpret_cast<char*>(&DestBuffer->at(0));

	FileStream.read(buffer, InfoHeader.PixelDataSize);

	if (FileStream.gcount() != InfoHeader.PixelDataSize) {
		return UNEXPECTED_END_OF_FILE;
	}

	return LOAD_SUCCESS;
}

/**
Calculates the byte length of a single row.

The BMP format requires the length of each row to be a multiple of 4.

@param[in] InfoHeader	The bitmap info header.

@return Returns the length of a row in bytes, taking possible padding into account.
*/
static std::uint32_t CalculateStride(const BitmapInfoHeader& InfoHeader)
{
	auto result = InfoHeader.Width * BYTES_PER_PIXEL;

	if (result % 4 != 0) {
		result += (4 - (result % 4));
	}

	return result;
}

/**
Flips the image rows and removes padding from the end.

The BMP format stores the rows of pixels in the image in reverse order, so the topmost row
is the last one in the file. This function reverses the order and removes padding from the end.

@param[in,out] DestBuffer	Pointer to the buffer containing the image data.
@param[in] InfoHeader		The bitmap info header.
*/
static void FlipRowsAndRemovePadding(std::vector<std::uint8_t>* DestBuffer, const BitmapInfoHeader& InfoHeader)
{
	std::vector<std::uint8_t> reversed;

	size_t numRows = InfoHeader.Height;
	size_t rowLength = InfoHeader.Width * BYTES_PER_PIXEL;
	reversed.resize(numRows * rowLength);

	// Stride is needed to advance in the source buffer.
	size_t stride = CalculateStride(InfoHeader);

	std::uint8_t* dest = &reversed[0];
	const std::uint8_t* src = DestBuffer->data() + (numRows - 1) * stride;

	for (size_t i = numRows; i > 0; i--) {
		std::memcpy(dest, src, rowLength);
		dest += rowLength;
		src -= stride;
	}

	DestBuffer->swap(reversed);
}

BitmapLoadStatus LoadBitmap(const char* Filename, std::vector<std::uint8_t>* DestBuffer,
							std::uint32_t* Width, std::uint32_t* Height)
{
	std::ifstream fileStream(Filename, std::ios::binary | std::ios::in);

	if (!fileStream) {
		return FILE_NOT_FOUND;
	}

	BitmapFileHeader fileHeader;
	BitmapInfoHeader infoHeader;

	auto status = LoadAndValidateHeaders(fileStream, &fileHeader, &infoHeader);
	if (status != LOAD_SUCCESS) {
		return status;
	}

	status = LoadPixels(fileStream, DestBuffer, fileHeader, infoHeader);
	if (status != LOAD_SUCCESS) {
		return status;
	}

	FlipRowsAndRemovePadding(DestBuffer, infoHeader);

	*Width = infoHeader.Width;
	*Height = infoHeader.Height;

	return LOAD_SUCCESS;
}

}<commit_msg>Formatting.<commit_after>#include "stdafx.h"
#include "bmp.h"

#include <fstream>
#include <cstring>

namespace
{

// These structures are not in bmp.h as they will not be exposed outside this file.
// Structure definitions taken from http://en.wikipedia.org/wiki/BMP_file_format

#pragma pack(1)	// Force 1-byte alignment.
struct BitmapFileHeader
{
    std::uint16_t   Magic;
    std::uint32_t   Filesize;
    std::uint16_t   Reserved1;
    std::uint16_t   Reserved2;
    std::uint32_t   PixelArrayOffset;
};

#pragma pack(1)	// Force 1-byte alignment.
struct BitmapInfoHeader
{
    std::uint32_t   HeaderSize;
    std::int32_t    Width;
    std::int32_t    Height;
    std::uint16_t   NumColorPlanes;
    std::uint16_t   NumBitsPerPixel;
    std::uint32_t   CompressionMethod;
    std::uint32_t   PixelDataSize;
    std::int32_t    HorizontalResolution;
    std::int32_t    VerticalResolution;
    std::uint32_t   NumPaletteColors;
    std::uint32_t   NumImportantColors;
};

}

namespace BMP
{

// The first two bytes of a BMP file are always 0x42 and 0x4D.
// 0x4D42 is the 16-bit little endian representation.
static const uint16_t BMP_MAGIC = 0x4D42;

// Only 24-bit images are supported.
static const uint32_t BYTES_PER_PIXEL = 3;

/**
Validates the bitmap file header.

@param[in] Header   The bitmap file header to be validated.

@return True if the file header is valid and the image can be loaded, otherwise false.
*/
static bool ValidateFileHeader(const BitmapFileHeader& Header)
{
    // TODO: Better validation.
    return (Header.Magic == BMP_MAGIC);
}

/**
Validates the bitmap info header.

This function checks if the following requirements are met:
\li Header size is 40 bytes
\li Image width and height are positive
\li Number of color planes is 1
\li Number of bits per pixel is 242
\li The image is uncompressed.

@param[in] Header   The bitmap info header to be validated.

@return True if the requirements are met and the image can be loaded, otherwise false.
*/
static bool ValidateInfoHeader(const BitmapInfoHeader& Header)
{
    return (Header.HeaderSize == sizeof(BitmapInfoHeader))
        && (Header.Width > 0)               // Width and height can be negative, but
        && (Header.Height > 0)              // we don't support those either.
        && (Header.NumColorPlanes == 1)     // 1 is the only valid value according to Wikipedia.
        && (Header.NumBitsPerPixel == 24)   // Only 24-bit images are supported.
        && (Header.CompressionMethod == 0); // Nonzero means compression is used.
}

/**
Loads and checks both bitmap file and info headers.

@param[in] FileStream   The input file stream.
@param[out] FileHeader  The bitmap file header read from the file.
@param[out] InfoHeader  The bitmap info header read from the file.

@return One of #BitmapLoadStatus codes.
*/
static BitmapLoadStatus LoadAndValidateHeaders(std::ifstream& FileStream, BitmapFileHeader* FileHeader,
                                               BitmapInfoHeader* InfoHeader)
{
    FileStream.read(reinterpret_cast<char*>(FileHeader), sizeof(*FileHeader));
    if (FileStream.bad()) {
        return INVALID_FILE_HEADER;
    }

    if (!ValidateFileHeader(*FileHeader)) {
        return INVALID_FILE_HEADER;
    }

    FileStream.read(reinterpret_cast<char*>(InfoHeader), sizeof(*InfoHeader));
    if (FileStream.bad() || !ValidateInfoHeader(*InfoHeader)) {
        return INVALID_INFO_HEADER;
    }

    return LOAD_SUCCESS;
}

/**
Reads the pixel rows from the bitmap file.

@param[in] FileStream   The input file stream.
@param[out] DestBuffer  The buffer to write the pixels to.
@param[in] FileHeader   The bitmap file header.
@param[in] InfoHeader   The bitmap info header.

@return One of #BitmapLoadStatus codes.
*/
static BitmapLoadStatus LoadPixels(std::ifstream& FileStream, std::vector<std::uint8_t>* DestBuffer,
                                   const BitmapFileHeader& FileHeader, const BitmapInfoHeader& InfoHeader)
{
    FileStream.seekg(FileHeader.PixelArrayOffset);

    DestBuffer->resize(InfoHeader.PixelDataSize);
    auto buffer = reinterpret_cast<char*>(&DestBuffer->at(0));

    FileStream.read(buffer, InfoHeader.PixelDataSize);

    if (FileStream.gcount() != InfoHeader.PixelDataSize) {
        return UNEXPECTED_END_OF_FILE;
    }

    return LOAD_SUCCESS;
}

/**
Calculates the byte length of a single row.

The BMP format requires the length of each row to be a multiple of 4.

@param[in] InfoHeader   The bitmap info header.

@return Returns the length of a row in bytes, taking possible padding into account.
*/
static std::uint32_t CalculateStride(const BitmapInfoHeader& InfoHeader)
{
    auto result = InfoHeader.Width * BYTES_PER_PIXEL;

    if (result % 4 != 0) {
        result += (4 - (result % 4));
    }

    return result;
}

/**
Flips the image rows and removes padding from the end.

The BMP format stores the rows of pixels in the image in reverse order, so the topmost row
is the last one in the file. This function reverses the order and removes padding from the end.

@param[in,out] DestBuffer   Pointer to the buffer containing the image data.
@param[in] InfoHeader       The bitmap info header.
*/
static void FlipRowsAndRemovePadding(std::vector<std::uint8_t>* DestBuffer, const BitmapInfoHeader& InfoHeader)
{
    std::vector<std::uint8_t> reversed;

    size_t numRows = InfoHeader.Height;
    size_t rowLength = InfoHeader.Width * BYTES_PER_PIXEL;
    reversed.resize(numRows * rowLength);

    // Stride is needed to advance in the source buffer.
    size_t stride = CalculateStride(InfoHeader);

    std::uint8_t* dest = &reversed[0];
    const std::uint8_t* src = DestBuffer->data() + (numRows - 1) * stride;

    for (size_t i = numRows; i > 0; i--) {
        std::memcpy(dest, src, rowLength);
        dest += rowLength;
        src -= stride;
    }

    DestBuffer->swap(reversed);
}

BitmapLoadStatus LoadBitmap(const char* Filename, std::vector<std::uint8_t>* DestBuffer,
                            std::uint32_t* Width, std::uint32_t* Height)
{
    std::ifstream fileStream(Filename, std::ios::binary | std::ios::in);

    if (!fileStream) {
        return FILE_NOT_FOUND;
    }

    BitmapFileHeader fileHeader;
    BitmapInfoHeader infoHeader;

    auto status = LoadAndValidateHeaders(fileStream, &fileHeader, &infoHeader);
    if (status != LOAD_SUCCESS) {
        return status;
    }

    status = LoadPixels(fileStream, DestBuffer, fileHeader, infoHeader);
    if (status != LOAD_SUCCESS) {
        return status;
    }

    FlipRowsAndRemovePadding(DestBuffer, infoHeader);

    *Width = infoHeader.Width;
    *Height = infoHeader.Height;

    return LOAD_SUCCESS;
}

}<|endoftext|>


<commit_before>#include <gtest/gtest.h>
#include <query.h>
#include <string.h>
#include <string>

using namespace std;

int main(int argc, char **argv) {
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

TEST(QueryTest, Simple)
{
Query query(new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" } }");

	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, And)
{
Query query2(new Where("c1", Equals, "10", new Where("c2", LessThan, "15")));
string json2;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\", \"and\" : { \"column\" : \"c2\", \"condition\" : \"<\", \"value\" : \"15\" } } }");

	json2 = query2.toJSON();
	ASSERT_EQ(json2.compare(expected), 0);
}

TEST(QueryTest, Aggregate)
{
Query query2(new Where("c1", Equals, "10", new Where("c2", LessThan, "15")));
string json2;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\", \"and\" : { \"column\" : \"c2\", \"condition\" : \"<\", \"value\" : \"15\" } }, \"aggregate\" : { \"column\" : \"c3\", \"operation\" : \"min\" } }");

	query2.aggregate(new Aggregate("min", "c3"));
	json2 = query2.toJSON();

	ASSERT_EQ(json2.compare(expected), 0);
}

TEST(QueryTest, AggregateList)
{
Query query2(new Where("c1", Equals, "10", new Where("c2", LessThan, "15")));
string json2;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\", \"and\" : { \"column\" : \"c2\", \"condition\" : \"<\", \"value\" : \"15\" } }, \"aggregate\" : [ { \"column\" : \"c3\", \"operation\" : \"min\" }, { \"column\" : \"c3\", \"operation\" : \"max\" } ] }");

	query2.aggregate(new Aggregate("min", "c3"));
	query2.aggregate(new Aggregate("max", "c3"));
	json2 = query2.toJSON();

	ASSERT_EQ(json2.compare(expected), 0);
}

TEST(QueryTest, Group)
{
Query query2(new Where("c1", Equals, "10", new Where("c2", LessThan, "15")));
string json2;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\", \"and\" : { \"column\" : \"c2\", \"condition\" : \"<\", \"value\" : \"15\" } }, \"aggregate\" : { \"column\" : \"c3\", \"operation\" : \"min\" }, \"group\" : \"c5\" }");

	query2.aggregate(new Aggregate("min", "c3"));
	query2.group("c5");
	json2 = query2.toJSON();

	ASSERT_EQ(json2.compare(expected), 0);
}

TEST(QueryTest, Sort)
{
Query query(new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"sort\" : { \"column\" : \"c2\", \"direction\" : \"asc\" } }");

	query.sort(new Sort("c2"));
	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, Sort2)
{
Query query(new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"sort\" : { \"column\" : \"c2\", \"direction\" : \"desc\" } }");

	query.sort(new Sort("c2", true));
	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, Limit)
{
Query query(new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"limit\" : 10 }");

	query.limit(10);
	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, Timebucket)
{
Query query(new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"timebucket\" : { \"timestamp\" : \"user_ts\", \"size\" : \"10\", \"format\" : \"DD-MM-YYYY HH:MI:SS\", \"alias\" : \"bucket\" } }");

	query.timebucket(new Timebucket("user_ts", 10, "DD-MM-YYYY HH:MI:SS", "bucket"));
	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, AggregateConstructor)
{
Query query2(new Aggregate("min", "c3"), new Where("c1", Equals, "10"));
string json2;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"aggregate\" : { \"column\" : \"c3\", \"operation\" : \"min\" } }");

	json2 = query2.toJSON();
	ASSERT_EQ(json2.compare(expected), 0);
}

TEST(QueryTest, TimebucketConstructor)
{
Query query(new Timebucket("user_ts", 10, "DD-MM-YYYY HH:MI:SS", "bucket"),
	    new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"timebucket\" : { \"timestamp\" : \"user_ts\", \"size\" : \"10\", \"format\" : \"DD-MM-YYYY HH:MI:SS\", \"alias\" : \"bucket\" } }");

	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, TimebucketConstructorLimit)
{
Query query(new Timebucket("user_ts", 10, "DD-MM-YYYY HH:MI:SS", "bucket"),
	    new Where("c1", Equals, "10"), 10);
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"timebucket\" : { \"timestamp\" : \"user_ts\", \"size\" : \"10\", \"format\" : \"DD-MM-YYYY HH:MI:SS\", \"alias\" : \"bucket\" }, \"limit\" : 10 }");

	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, SingleReturn)
{
Query query(new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"returns\" : [ \"c2\" ] }");

	query.returns(new Returns("c2"));
	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, MultipleReturn)
{
Query query(new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"returns\" : [ \"c1\", \"c2\", \"c3\" ] }");

	query.returns(new Returns("c1"));
	query.returns(new Returns("c2"));
	query.returns(new Returns("c3"));
	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, MultipleReturn2)
{
Query query(new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"returns\" : [ \"c1\", { \"column\" : \"c2\", \"alias\" : \"Col2\" }, { \"column\" : \"c3\", \"alias\" : \"Col3\", \"format\" : \"DD-MM-YY HH:MI:SS\" } ] }");

	query.returns(new Returns("c1"));
	query.returns(new Returns("c2", "Col2"));
	query.returns(new Returns("c3", "Col3", "DD-MM-YY HH:MI:SS"));
	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, MultipleReturnVector)
{
Query query(new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"returns\" : [ \"c1\", \"c2\", \"c3\" ] }");

	query.returns(vector<Returns *> {new Returns("c1"),
				         new Returns("c2"),
				         new Returns("c3")});
	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, MultipleReturnConstrcutor)
{
Query query(vector<Returns *> {new Returns("c1"),
			       new Returns("c2"),
			       new Returns("c3")},
            new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"returns\" : [ \"c1\", \"c2\", \"c3\" ] }");

	json = query.toJSON();
printf("Expected: %s\n", expected.c_str());
printf("Got:      %s\n", json.c_str());
	ASSERT_EQ(json.compare(expected), 0);
}
<commit_msg>FOGL-1174 Unit test update<commit_after>#include <gtest/gtest.h>
#include <query.h>
#include <string.h>
#include <string>

using namespace std;

int main(int argc, char **argv) {
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

TEST(QueryTest, Simple)
{
Query query(new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" } }");

	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, And)
{
Query query2(new Where("c1", Equals, "10", new Where("c2", LessThan, "15")));
string json2;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\", \"and\" : { \"column\" : \"c2\", \"condition\" : \"<\", \"value\" : \"15\" } } }");

	json2 = query2.toJSON();
	ASSERT_EQ(json2.compare(expected), 0);
}

TEST(QueryTest, Aggregate)
{
Query query2(new Where("c1", Equals, "10", new Where("c2", LessThan, "15")));
string json2;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\", \"and\" : { \"column\" : \"c2\", \"condition\" : \"<\", \"value\" : \"15\" } }, \"aggregate\" : { \"column\" : \"c3\", \"operation\" : \"min\" } }");

	query2.aggregate(new Aggregate("min", "c3"));
	json2 = query2.toJSON();

	ASSERT_EQ(json2.compare(expected), 0);
}

TEST(QueryTest, AggregateList)
{
Query query2(new Where("c1", Equals, "10", new Where("c2", LessThan, "15")));
string json2;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\", \"and\" : { \"column\" : \"c2\", \"condition\" : \"<\", \"value\" : \"15\" } }, \"aggregate\" : [ { \"column\" : \"c3\", \"operation\" : \"min\" }, { \"column\" : \"c3\", \"operation\" : \"max\" } ] }");

	query2.aggregate(new Aggregate("min", "c3"));
	query2.aggregate(new Aggregate("max", "c3"));
	json2 = query2.toJSON();

	ASSERT_EQ(json2.compare(expected), 0);
}

TEST(QueryTest, Group)
{
Query query2(new Where("c1", Equals, "10", new Where("c2", LessThan, "15")));
string json2;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\", \"and\" : { \"column\" : \"c2\", \"condition\" : \"<\", \"value\" : \"15\" } }, \"aggregate\" : { \"column\" : \"c3\", \"operation\" : \"min\" }, \"group\" : \"c5\" }");

	query2.aggregate(new Aggregate("min", "c3"));
	query2.group("c5");
	json2 = query2.toJSON();

	ASSERT_EQ(json2.compare(expected), 0);
}

TEST(QueryTest, Sort)
{
Query query(new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"sort\" : { \"column\" : \"c2\", \"direction\" : \"asc\" } }");

	query.sort(new Sort("c2"));
	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, Sort2)
{
Query query(new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"sort\" : { \"column\" : \"c2\", \"direction\" : \"desc\" } }");

	query.sort(new Sort("c2", true));
	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, Limit)
{
Query query(new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"limit\" : 10 }");

	query.limit(10);
	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, Timebucket)
{
Query query(new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"timebucket\" : { \"timestamp\" : \"user_ts\", \"size\" : \"10\", \"format\" : \"DD-MM-YYYY HH:MI:SS\", \"alias\" : \"bucket\" } }");

	query.timebucket(new Timebucket("user_ts", 10, "DD-MM-YYYY HH:MI:SS", "bucket"));
	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, AggregateConstructor)
{
Query query2(new Aggregate("min", "c3"), new Where("c1", Equals, "10"));
string json2;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"aggregate\" : { \"column\" : \"c3\", \"operation\" : \"min\" } }");

	json2 = query2.toJSON();
	ASSERT_EQ(json2.compare(expected), 0);
}

TEST(QueryTest, TimebucketConstructor)
{
Query query(new Timebucket("user_ts", 10, "DD-MM-YYYY HH:MI:SS", "bucket"),
	    new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"timebucket\" : { \"timestamp\" : \"user_ts\", \"size\" : \"10\", \"format\" : \"DD-MM-YYYY HH:MI:SS\", \"alias\" : \"bucket\" } }");

	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, TimebucketConstructorLimit)
{
Query query(new Timebucket("user_ts", 10, "DD-MM-YYYY HH:MI:SS", "bucket"),
	    new Where("c1", Equals, "10"), 10);
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"timebucket\" : { \"timestamp\" : \"user_ts\", \"size\" : \"10\", \"format\" : \"DD-MM-YYYY HH:MI:SS\", \"alias\" : \"bucket\" }, \"limit\" : 10 }");

	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, SingleReturn)
{
Query query(new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"returns\" : [ \"c2\" ] }");

	query.returns(new Returns("c2"));
	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, MultipleReturn)
{
Query query(new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"returns\" : [ \"c1\", \"c2\", \"c3\" ] }");

	query.returns(new Returns("c1"));
	query.returns(new Returns("c2"));
	query.returns(new Returns("c3"));
	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, MultipleReturn2)
{
Query query(new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"returns\" : [ \"c1\", { \"column\" : \"c2\", \"alias\" : \"Col2\" }, { \"column\" : \"c3\", \"alias\" : \"Col3\", \"format\" : \"DD-MM-YY HH:MI:SS\" } ] }");

	query.returns(new Returns("c1"));
	query.returns(new Returns("c2", "Col2"));
	query.returns(new Returns("c3", "Col3", "DD-MM-YY HH:MI:SS"));
	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, MultipleReturnVector)
{
Query query(new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"returns\" : [ \"c1\", \"c2\", \"c3\" ] }");

	query.returns(vector<Returns *> {new Returns("c1"),
				         new Returns("c2"),
				         new Returns("c3")});
	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}

TEST(QueryTest, MultipleReturnConstrcutor)
{
Query query(vector<Returns *> {new Returns("c1"),
			       new Returns("c2"),
			       new Returns("c3")},
            new Where("c1", Equals, "10"));
string json;
string expected("{ \"where\" : { \"column\" : \"c1\", \"condition\" : \"=\", \"value\" : \"10\" }, \"returns\" : [ \"c1\", \"c2\", \"c3\" ] }");

	json = query.toJSON();
	ASSERT_EQ(json.compare(expected), 0);
}
<|endoftext|>


<commit_before>#include "persistentmodel/job.h"

WARNINGS_DISABLE
#include <QDateTime>
#include <QDir>
#include <QFileInfo>
#include <QSqlError>
#include <QSqlQuery>
#include <QSqlRecord>
#include <QStringList>
#include <QUrl>
#include <QVariant>
WARNINGS_ENABLE

#include "TSettings.h"

#include "backuptask.h"
#include "debug.h"
#include "persistentmodel/archive.h"
#include "persistentmodel/persistentstore.h"
#include "tasks/tasks-defs.h"
#include "utils.h"

Job::Job(QObject *parent)
    : PersistentObject(parent),
      _optionScheduledEnabled(JobSchedule::Disabled),
      _optionPreservePaths(DEFAULT_PRESERVE_PATHNAMES),
      _optionTraverseMount(DEFAULT_TRAVERSE_MOUNT),
      _optionFollowSymLinks(DEFAULT_FOLLOW_SYMLINKS),
      _optionSkipFilesSize(DEFAULT_SKIP_FILES_SIZE),
      _optionSkipFiles(DEFAULT_SKIP_SYSTEM_ENABLED),
      _optionSkipFilesPatterns(DEFAULT_SKIP_SYSTEM_FILES),
      _optionSkipNoDump(DEFAULT_SKIP_NODUMP),
      _settingShowHidden(false),
      _settingShowSystem(false),
      _settingHideSymlinks(false)
{
    // Load values from settings.
    TSettings settings;
    setOptionPreservePaths(
        settings.value("tarsnap/preserve_pathnames", optionPreservePaths())
            .toBool());
    setOptionTraverseMount(
        settings.value("tarsnap/traverse_mount", optionTraverseMount())
            .toBool());
    setOptionFollowSymLinks(
        settings.value("tarsnap/follow_symlinks", optionFollowSymLinks())
            .toBool());
    setOptionSkipNoDump(
        settings.value("app/skip_nodump", optionSkipNoDump()).toBool());
    setOptionSkipFilesSize(
        settings.value("app/skip_files_size", optionSkipFilesSize()).toInt());
    setOptionSkipFiles(
        settings.value("app/skip_system_enabled", optionSkipFiles()).toBool());
    setOptionSkipFilesPatterns(
        settings.value("app/skip_system_files", optionSkipFilesPatterns())
            .toString());
}

Job::~Job()
{
}

QString Job::name() const
{
    return _name;
}

void Job::setName(const QString &name)
{
    _name = name;
}

QString Job::archivePrefix()
{
    return JOB_NAME_PREFIX + name();
}

QList<QUrl> Job::urls() const
{
    return _urls;
}

void Job::setUrls(const QList<QUrl> &urls)
{
    _urls = urls;
}

bool Job::validateUrls()
{
    // Check that _urls is not empty.
    if(_urls.isEmpty())
        return false;
    // Check that every file exists.
    for(const QUrl &url : _urls)
    {
        QFileInfo file(url.toLocalFile());
        if(!file.exists())
            return false;
    }
    return true;
}

void Job::installWatcher()
{
    connect(&_fsWatcher, &QFileSystemWatcher::directoryChanged, this,
            &Job::fsEvent);
    connect(&_fsWatcher, &QFileSystemWatcher::fileChanged, this, &Job::fsEvent);

    for(const QUrl &url : _urls)
    {
        // Emit a signal if a file has changed.
        QFileInfo file(url.toLocalFile());
        _fsWatcher.addPath(file.absoluteFilePath());
        // Emit a signal if a directory or any of its ancestors has changed.
        QDir dir(file.absoluteDir());
        while(dir != QDir::root())
        {
            _fsWatcher.addPath(dir.absolutePath());
            dir.cdUp();
        }
    }
}

void Job::removeWatcher()
{
    disconnect(&_fsWatcher, &QFileSystemWatcher::directoryChanged, this,
               &Job::fsEvent);
    disconnect(&_fsWatcher, &QFileSystemWatcher::fileChanged, this,
               &Job::fsEvent);

    QStringList watching = _fsWatcher.files() + _fsWatcher.directories();
    if(!watching.isEmpty())
        _fsWatcher.removePaths(watching);
}

QList<ArchivePtr> Job::archives() const
{
    return _archives;
}

void Job::setArchives(const QList<ArchivePtr> &archives)
{
    _archives.clear();
    _archives = archives;
    // Sort archives based on timestamp using an anonymous sorting function.
    std::sort(_archives.begin(), _archives.end(),
              [](const ArchivePtr &a, const ArchivePtr &b) {
                  return (a->timestamp() > b->timestamp());
              });
    // If any Archive is deleted, reload the list.
    for(const ArchivePtr &archive : _archives)
    {
        connect(archive.data(), &Archive::purged, this, &Job::loadArchives,
                QUEUED);
    }
    emit changed();
}

JobSchedule Job::optionScheduledEnabled() const
{
    return static_cast<JobSchedule>(_optionScheduledEnabled);
}

void Job::setOptionScheduledEnabled(JobSchedule schedule)
{
    _optionScheduledEnabled = schedule;
}

bool Job::optionPreservePaths() const
{
    return _optionPreservePaths;
}

void Job::setOptionPreservePaths(bool optionPreservePaths)
{
    _optionPreservePaths = optionPreservePaths;
}

bool Job::optionFollowSymLinks() const
{
    return _optionFollowSymLinks;
}

void Job::setOptionFollowSymLinks(bool optionFollowSymLinks)
{
    _optionFollowSymLinks = optionFollowSymLinks;
}

bool Job::optionTraverseMount() const
{
    return _optionTraverseMount;
}

void Job::setOptionTraverseMount(bool optionTraverseMount)
{
    _optionTraverseMount = optionTraverseMount;
}

int Job::optionSkipFilesSize() const
{
    return _optionSkipFilesSize;
}

void Job::setOptionSkipFilesSize(const int &optionSkipFilesSize)
{
    _optionSkipFilesSize = optionSkipFilesSize;
}

bool Job::optionSkipFiles() const
{
    return _optionSkipFiles;
}

void Job::setOptionSkipFiles(bool optionSkipFiles)
{
    _optionSkipFiles = optionSkipFiles;
}

QString Job::optionSkipFilesPatterns() const
{
    return _optionSkipFilesPatterns;
}

void Job::setOptionSkipFilesPatterns(const QString &optionSkipFilesPatterns)
{
    _optionSkipFilesPatterns = optionSkipFilesPatterns;
}

bool Job::optionSkipNoDump() const
{
    return _optionSkipNoDump;
}

void Job::setOptionSkipNoDump(bool optionSkipNoDump)
{
    _optionSkipNoDump = optionSkipNoDump;
}

bool Job::settingShowHidden() const
{
    return _settingShowHidden;
}

void Job::setSettingShowHidden(bool settingShowHidden)
{
    _settingShowHidden = settingShowHidden;
}

bool Job::settingShowSystem() const
{
    return _settingShowSystem;
}

void Job::setSettingShowSystem(bool settingShowSystem)
{
    _settingShowSystem = settingShowSystem;
}

bool Job::settingHideSymlinks() const
{
    return _settingHideSymlinks;
}

void Job::setSettingHideSymlinks(bool settingHideSymlinks)
{
    _settingHideSymlinks = settingHideSymlinks;
}

void Job::save()
{
    bool exists = doesKeyExist(_name);

    // Prepare query: either updating or creating an entry.
    QString queryString;
    if(exists)
        queryString = QLatin1String(
            "update jobs set name=?, urls=?, optionScheduledEnabled=?,"
            " optionPreservePaths=?, optionTraverseMount=?,"
            " optionFollowSymLinks=?, optionSkipFilesSize=?,"
            " optionSkipFiles=?, optionSkipFilesPatterns=?,"
            " optionSkipNoDump=?, settingShowHidden=?, settingShowSystem=?,"
            " settingHideSymlinks=?"
            " where name=?");
    else
        queryString = QLatin1String(
            "insert into jobs(name, urls, optionScheduledEnabled,"
            " optionPreservePaths, optionTraverseMount,"
            " optionFollowSymLinks, optionSkipFilesSize, optionSkipFiles,"
            " optionSkipFilesPatterns, optionSkipNoDump, settingShowHidden,"
            " settingShowSystem, settingHideSymlinks)"
            " values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");

    // Get database instance and create query object.
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(queryString))
    {
        DEBUG << query.lastError().text();
        return;
    }
    // Fill in missing value in query string.
    query.addBindValue(_name);
    QStringList query_urls;
    for(const QUrl &url : _urls)
        query_urls << url.toString(QUrl::FullyEncoded);
    query.addBindValue(query_urls.join('\n'));
    query.addBindValue(_optionScheduledEnabled);
    query.addBindValue(_optionPreservePaths);
    query.addBindValue(_optionTraverseMount);
    query.addBindValue(_optionFollowSymLinks);
    query.addBindValue(_optionSkipFilesSize);
    query.addBindValue(_optionSkipFiles);
    query.addBindValue(_optionSkipFilesPatterns);
    query.addBindValue(_optionSkipNoDump);
    query.addBindValue(_settingShowHidden);
    query.addBindValue(_settingShowSystem);
    query.addBindValue(_settingHideSymlinks);
    if(exists)
        query.addBindValue(_name);

    // Run query.
    if(!global_store->runQuery(query))
        DEBUG << "Failed to save Job entry.";
    setObjectKey(_name);
}

void Job::load()
{
    // Sanity checks.
    if(_name.isEmpty())
    {
        DEBUG << "Attempting to load Job object with empty _name key.";
        return;
    }
    // Get database instance and prepare query.
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(QLatin1String("select * from jobs where name = ?")))
    {
        DEBUG << query.lastError().text();
        return;
    }
    // Fill in missing value in query string.
    query.addBindValue(_name);
    // Run query.
    if(global_store->runQuery(query) && query.next())
    {
        _urls = QUrl::fromStringList(query.value(query.record().indexOf("urls"))
                                         .toString()
                                         .split('\n', QString::SkipEmptyParts));
        _optionScheduledEnabled =
            query.value(query.record().indexOf("optionScheduledEnabled"))
                .toInt();
        _optionPreservePaths =
            query.value(query.record().indexOf("optionPreservePaths")).toBool();
        _optionTraverseMount =
            query.value(query.record().indexOf("optionTraverseMount")).toBool();
        _optionFollowSymLinks =
            query.value(query.record().indexOf("optionFollowSymLinks"))
                .toBool();
        _optionSkipFilesSize =
            query.value(query.record().indexOf("optionSkipFilesSize")).toInt();
        _optionSkipFiles =
            query.value(query.record().indexOf("optionSkipFiles")).toBool();
        _optionSkipFilesPatterns =
            query.value(query.record().indexOf("optionSkipFilesPatterns"))
                .toString();
        _optionSkipNoDump =
            query.value(query.record().indexOf("optionSkipNoDump")).toBool();
        _settingShowHidden =
            query.value(query.record().indexOf("settingShowHidden")).toBool();
        _settingShowSystem =
            query.value(query.record().indexOf("settingShowSystem")).toBool();
        _settingHideSymlinks =
            query.value(query.record().indexOf("settingHideSymlinks")).toBool();
        setObjectKey(_name);
        emit loadArchives();
    }
    else
    {
        DEBUG << "Job object with key " << _name << " not found.";
    }
}

void Job::purge()
{
    // Sanity checks.
    if(_name.isEmpty())
    {
        DEBUG << "Attempting to delete Job object with empty _name key.";
        return;
    }
    if(!doesKeyExist(_name))
    {
        DEBUG << "No Job object with key " << _name;
        return;
    }
    // Get database instance and prepare query.
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(QLatin1String("delete from jobs where name = ?")))
    {
        DEBUG << query.lastError().text();
        return;
    }
    // Fill in missing value in query string.
    query.addBindValue(_name);
    // Run query.
    if(!global_store->runQuery(query))
        DEBUG << "Failed to remove Job entry.";
    setObjectKey("");
    emit purged();
}

bool Job::doesKeyExist(QString key)
{
    // Sanity check.
    if(key.isEmpty())
    {
        DEBUG << "doesKeyExist method called with empty args";
        return false;
    }
    // Get database instance and prepare query.
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(QLatin1String("select name from jobs where name = ?")))
    {
        DEBUG << query.lastError().text();
        return false;
    }
    // Fill in missing value in query string.
    query.addBindValue(key);
    // Run query.
    if(global_store->runQuery(query))
    {
        if(query.next())
            return true;
    }
    else
    {
        DEBUG << "Failed to run doesKeyExist query for a Job.";
    }
    return false;
}
<commit_msg>Job: clarify "urls to string" in save()<commit_after>#include "persistentmodel/job.h"

WARNINGS_DISABLE
#include <QDateTime>
#include <QDir>
#include <QFileInfo>
#include <QSqlError>
#include <QSqlQuery>
#include <QSqlRecord>
#include <QStringList>
#include <QUrl>
#include <QVariant>
WARNINGS_ENABLE

#include "TSettings.h"

#include "backuptask.h"
#include "debug.h"
#include "persistentmodel/archive.h"
#include "persistentmodel/persistentstore.h"
#include "tasks/tasks-defs.h"
#include "utils.h"

Job::Job(QObject *parent)
    : PersistentObject(parent),
      _optionScheduledEnabled(JobSchedule::Disabled),
      _optionPreservePaths(DEFAULT_PRESERVE_PATHNAMES),
      _optionTraverseMount(DEFAULT_TRAVERSE_MOUNT),
      _optionFollowSymLinks(DEFAULT_FOLLOW_SYMLINKS),
      _optionSkipFilesSize(DEFAULT_SKIP_FILES_SIZE),
      _optionSkipFiles(DEFAULT_SKIP_SYSTEM_ENABLED),
      _optionSkipFilesPatterns(DEFAULT_SKIP_SYSTEM_FILES),
      _optionSkipNoDump(DEFAULT_SKIP_NODUMP),
      _settingShowHidden(false),
      _settingShowSystem(false),
      _settingHideSymlinks(false)
{
    // Load values from settings.
    TSettings settings;
    setOptionPreservePaths(
        settings.value("tarsnap/preserve_pathnames", optionPreservePaths())
            .toBool());
    setOptionTraverseMount(
        settings.value("tarsnap/traverse_mount", optionTraverseMount())
            .toBool());
    setOptionFollowSymLinks(
        settings.value("tarsnap/follow_symlinks", optionFollowSymLinks())
            .toBool());
    setOptionSkipNoDump(
        settings.value("app/skip_nodump", optionSkipNoDump()).toBool());
    setOptionSkipFilesSize(
        settings.value("app/skip_files_size", optionSkipFilesSize()).toInt());
    setOptionSkipFiles(
        settings.value("app/skip_system_enabled", optionSkipFiles()).toBool());
    setOptionSkipFilesPatterns(
        settings.value("app/skip_system_files", optionSkipFilesPatterns())
            .toString());
}

Job::~Job()
{
}

QString Job::name() const
{
    return _name;
}

void Job::setName(const QString &name)
{
    _name = name;
}

QString Job::archivePrefix()
{
    return JOB_NAME_PREFIX + name();
}

QList<QUrl> Job::urls() const
{
    return _urls;
}

void Job::setUrls(const QList<QUrl> &urls)
{
    _urls = urls;
}

bool Job::validateUrls()
{
    // Check that _urls is not empty.
    if(_urls.isEmpty())
        return false;
    // Check that every file exists.
    for(const QUrl &url : _urls)
    {
        QFileInfo file(url.toLocalFile());
        if(!file.exists())
            return false;
    }
    return true;
}

void Job::installWatcher()
{
    connect(&_fsWatcher, &QFileSystemWatcher::directoryChanged, this,
            &Job::fsEvent);
    connect(&_fsWatcher, &QFileSystemWatcher::fileChanged, this, &Job::fsEvent);

    for(const QUrl &url : _urls)
    {
        // Emit a signal if a file has changed.
        QFileInfo file(url.toLocalFile());
        _fsWatcher.addPath(file.absoluteFilePath());
        // Emit a signal if a directory or any of its ancestors has changed.
        QDir dir(file.absoluteDir());
        while(dir != QDir::root())
        {
            _fsWatcher.addPath(dir.absolutePath());
            dir.cdUp();
        }
    }
}

void Job::removeWatcher()
{
    disconnect(&_fsWatcher, &QFileSystemWatcher::directoryChanged, this,
               &Job::fsEvent);
    disconnect(&_fsWatcher, &QFileSystemWatcher::fileChanged, this,
               &Job::fsEvent);

    QStringList watching = _fsWatcher.files() + _fsWatcher.directories();
    if(!watching.isEmpty())
        _fsWatcher.removePaths(watching);
}

QList<ArchivePtr> Job::archives() const
{
    return _archives;
}

void Job::setArchives(const QList<ArchivePtr> &archives)
{
    _archives.clear();
    _archives = archives;
    // Sort archives based on timestamp using an anonymous sorting function.
    std::sort(_archives.begin(), _archives.end(),
              [](const ArchivePtr &a, const ArchivePtr &b) {
                  return (a->timestamp() > b->timestamp());
              });
    // If any Archive is deleted, reload the list.
    for(const ArchivePtr &archive : _archives)
    {
        connect(archive.data(), &Archive::purged, this, &Job::loadArchives,
                QUEUED);
    }
    emit changed();
}

JobSchedule Job::optionScheduledEnabled() const
{
    return static_cast<JobSchedule>(_optionScheduledEnabled);
}

void Job::setOptionScheduledEnabled(JobSchedule schedule)
{
    _optionScheduledEnabled = schedule;
}

bool Job::optionPreservePaths() const
{
    return _optionPreservePaths;
}

void Job::setOptionPreservePaths(bool optionPreservePaths)
{
    _optionPreservePaths = optionPreservePaths;
}

bool Job::optionFollowSymLinks() const
{
    return _optionFollowSymLinks;
}

void Job::setOptionFollowSymLinks(bool optionFollowSymLinks)
{
    _optionFollowSymLinks = optionFollowSymLinks;
}

bool Job::optionTraverseMount() const
{
    return _optionTraverseMount;
}

void Job::setOptionTraverseMount(bool optionTraverseMount)
{
    _optionTraverseMount = optionTraverseMount;
}

int Job::optionSkipFilesSize() const
{
    return _optionSkipFilesSize;
}

void Job::setOptionSkipFilesSize(const int &optionSkipFilesSize)
{
    _optionSkipFilesSize = optionSkipFilesSize;
}

bool Job::optionSkipFiles() const
{
    return _optionSkipFiles;
}

void Job::setOptionSkipFiles(bool optionSkipFiles)
{
    _optionSkipFiles = optionSkipFiles;
}

QString Job::optionSkipFilesPatterns() const
{
    return _optionSkipFilesPatterns;
}

void Job::setOptionSkipFilesPatterns(const QString &optionSkipFilesPatterns)
{
    _optionSkipFilesPatterns = optionSkipFilesPatterns;
}

bool Job::optionSkipNoDump() const
{
    return _optionSkipNoDump;
}

void Job::setOptionSkipNoDump(bool optionSkipNoDump)
{
    _optionSkipNoDump = optionSkipNoDump;
}

bool Job::settingShowHidden() const
{
    return _settingShowHidden;
}

void Job::setSettingShowHidden(bool settingShowHidden)
{
    _settingShowHidden = settingShowHidden;
}

bool Job::settingShowSystem() const
{
    return _settingShowSystem;
}

void Job::setSettingShowSystem(bool settingShowSystem)
{
    _settingShowSystem = settingShowSystem;
}

bool Job::settingHideSymlinks() const
{
    return _settingHideSymlinks;
}

void Job::setSettingHideSymlinks(bool settingHideSymlinks)
{
    _settingHideSymlinks = settingHideSymlinks;
}

void Job::save()
{
    bool exists = doesKeyExist(_name);

    // Prepare query: either updating or creating an entry.
    QString queryString;
    if(exists)
        queryString = QLatin1String(
            "update jobs set name=?, urls=?, optionScheduledEnabled=?,"
            " optionPreservePaths=?, optionTraverseMount=?,"
            " optionFollowSymLinks=?, optionSkipFilesSize=?,"
            " optionSkipFiles=?, optionSkipFilesPatterns=?,"
            " optionSkipNoDump=?, settingShowHidden=?, settingShowSystem=?,"
            " settingHideSymlinks=?"
            " where name=?");
    else
        queryString = QLatin1String(
            "insert into jobs(name, urls, optionScheduledEnabled,"
            " optionPreservePaths, optionTraverseMount,"
            " optionFollowSymLinks, optionSkipFilesSize, optionSkipFiles,"
            " optionSkipFilesPatterns, optionSkipNoDump, settingShowHidden,"
            " settingShowSystem, settingHideSymlinks)"
            " values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");

    // Get database instance and create query object.
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(queryString))
    {
        DEBUG << query.lastError().text();
        return;
    }
    // Convert urls to a string.
    QStringList query_urls;
    for(const QUrl &url : _urls)
        query_urls << url.toString(QUrl::FullyEncoded);
    const QString url_str = query_urls.join('\n');

    // Fill in missing values in query string.
    query.addBindValue(_name);
    query.addBindValue(url_str);
    query.addBindValue(_optionScheduledEnabled);
    query.addBindValue(_optionPreservePaths);
    query.addBindValue(_optionTraverseMount);
    query.addBindValue(_optionFollowSymLinks);
    query.addBindValue(_optionSkipFilesSize);
    query.addBindValue(_optionSkipFiles);
    query.addBindValue(_optionSkipFilesPatterns);
    query.addBindValue(_optionSkipNoDump);
    query.addBindValue(_settingShowHidden);
    query.addBindValue(_settingShowSystem);
    query.addBindValue(_settingHideSymlinks);
    if(exists)
        query.addBindValue(_name);

    // Run query.
    if(!global_store->runQuery(query))
        DEBUG << "Failed to save Job entry.";
    setObjectKey(_name);
}

void Job::load()
{
    // Sanity checks.
    if(_name.isEmpty())
    {
        DEBUG << "Attempting to load Job object with empty _name key.";
        return;
    }
    // Get database instance and prepare query.
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(QLatin1String("select * from jobs where name = ?")))
    {
        DEBUG << query.lastError().text();
        return;
    }
    // Fill in missing value in query string.
    query.addBindValue(_name);
    // Run query.
    if(global_store->runQuery(query) && query.next())
    {
        _urls = QUrl::fromStringList(query.value(query.record().indexOf("urls"))
                                         .toString()
                                         .split('\n', QString::SkipEmptyParts));
        _optionScheduledEnabled =
            query.value(query.record().indexOf("optionScheduledEnabled"))
                .toInt();
        _optionPreservePaths =
            query.value(query.record().indexOf("optionPreservePaths")).toBool();
        _optionTraverseMount =
            query.value(query.record().indexOf("optionTraverseMount")).toBool();
        _optionFollowSymLinks =
            query.value(query.record().indexOf("optionFollowSymLinks"))
                .toBool();
        _optionSkipFilesSize =
            query.value(query.record().indexOf("optionSkipFilesSize")).toInt();
        _optionSkipFiles =
            query.value(query.record().indexOf("optionSkipFiles")).toBool();
        _optionSkipFilesPatterns =
            query.value(query.record().indexOf("optionSkipFilesPatterns"))
                .toString();
        _optionSkipNoDump =
            query.value(query.record().indexOf("optionSkipNoDump")).toBool();
        _settingShowHidden =
            query.value(query.record().indexOf("settingShowHidden")).toBool();
        _settingShowSystem =
            query.value(query.record().indexOf("settingShowSystem")).toBool();
        _settingHideSymlinks =
            query.value(query.record().indexOf("settingHideSymlinks")).toBool();
        setObjectKey(_name);
        emit loadArchives();
    }
    else
    {
        DEBUG << "Job object with key " << _name << " not found.";
    }
}

void Job::purge()
{
    // Sanity checks.
    if(_name.isEmpty())
    {
        DEBUG << "Attempting to delete Job object with empty _name key.";
        return;
    }
    if(!doesKeyExist(_name))
    {
        DEBUG << "No Job object with key " << _name;
        return;
    }
    // Get database instance and prepare query.
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(QLatin1String("delete from jobs where name = ?")))
    {
        DEBUG << query.lastError().text();
        return;
    }
    // Fill in missing value in query string.
    query.addBindValue(_name);
    // Run query.
    if(!global_store->runQuery(query))
        DEBUG << "Failed to remove Job entry.";
    setObjectKey("");
    emit purged();
}

bool Job::doesKeyExist(QString key)
{
    // Sanity check.
    if(key.isEmpty())
    {
        DEBUG << "doesKeyExist method called with empty args";
        return false;
    }
    // Get database instance and prepare query.
    QSqlQuery query = global_store->createQuery();
    if(!query.prepare(QLatin1String("select name from jobs where name = ?")))
    {
        DEBUG << query.lastError().text();
        return false;
    }
    // Fill in missing value in query string.
    query.addBindValue(key);
    // Run query.
    if(global_store->runQuery(query))
    {
        if(query.next())
            return true;
    }
    else
    {
        DEBUG << "Failed to run doesKeyExist query for a Job.";
    }
    return false;
}
<|endoftext|>


<commit_before>//
// Test Suite for geos::geom::LineString class.

// tut
#include <tut/tut.hpp>
#include <utility.h>
// geos
#include <geos/geom/LineString.h>
#include <geos/geom/Coordinate.h>
#include <geos/geom/CoordinateArraySequence.h>
#include <geos/geom/Dimension.h>
#include <geos/geom/Geometry.h>
#include <geos/geom/GeometryFactory.h>
#include <geos/geom/PrecisionModel.h>
#include <geos/io/WKTReader.h>
#include <geos/util/GEOSException.h>
#include <geos/util/IllegalArgumentException.h>
// std
#include <string>
#include <cmath>
#include <cassert>

namespace tut {
//
// Test Group
//

// Common data used by tests
struct test_linestring_data {
    // Typedefs used as short names by test cases
    typedef std::unique_ptr<geos::geom::LineString> LineStringAutoPtr;

    geos::geom::PrecisionModel pm_;
    geos::geom::GeometryFactory::Ptr factory_;
    geos::io::WKTReader reader_;

    LineStringPtr empty_line_;

    test_linestring_data()
        : pm_(1000)
        , factory_(geos::geom::GeometryFactory::create(&pm_, 0))
        , reader_(factory_.get())
        , empty_line_(factory_->createLineString(new geos::geom::CoordinateArraySequence()))
    {
        assert(nullptr != empty_line_);
    }

    ~test_linestring_data()
    {
        factory_->destroyGeometry(empty_line_);
        empty_line_ = nullptr;
    }
};

typedef test_group<test_linestring_data> group;
typedef group::object object;

group test_linestring_group("geos::geom::LineString");

//
// Test Cases
//

// Test of empty LineString created with user's constructor
template<>
template<>
void object::test<1>
()
{
    using geos::geom::Coordinate;

    // Empty sequence of coordiantes
    CoordArrayPtr pseq = new geos::geom::CoordinateArraySequence();
    ensure("sequence is null pointer.", pseq != nullptr);

    // Create empty linstring instance
    LineStringAutoPtr ls(factory_->createLineString(pseq));

    ensure(ls->isEmpty());
    ensure(ls->isSimple());
    ensure(ls->isValid());
}

// Test of non-tempty LineString created with user's constructor
template<>
template<>
void object::test<2>
()
{
    using geos::geom::Coordinate;

    // Non-empty sequence of coordiantes
    const size_t size3 = 3;

    CoordArrayPtr pseq = new geos::geom::CoordinateArraySequence();
    ensure("sequence is null pointer.", pseq != nullptr);

    pseq->add(Coordinate(0, 0, 0));
    pseq->add(Coordinate(5, 5, 5));
    pseq->add(Coordinate(10, 10, 10));

    ensure_equals(pseq->size(), size3);

    // Create non-empty linstring instance
    LineStringAutoPtr ls(factory_->createLineString(pseq));

    ensure(!ls->isEmpty());
    ensure(ls->isSimple());
    ensure(ls->isValid());

    ensure(!ls->isClosed());
    ensure(!ls->isRing());

    std::unique_ptr<geos::geom::Geometry> geo;
    geo = ls->getEnvelope();
    ensure(geo != nullptr);
    ensure(!geo->isEmpty());

    geo = ls->getBoundary();
    ensure(geo != nullptr);
    ensure(!geo->isEmpty());

    geo = ls->convexHull();
    ensure(geo != nullptr);
    ensure(!geo->isEmpty());

    ensure_equals(ls->getGeometryTypeId(), geos::geom::GEOS_LINESTRING);
    ensure_equals(ls->getDimension(), geos::geom::Dimension::L);
    ensure_equals(ls->getBoundaryDimension(), geos::geom::Dimension::P);
    ensure_equals(ls->getCoordinateDimension(), 3);
    ensure_equals(ls->getNumPoints(), size3);
    ensure_equals(ls->getArea(), 0.0);
    ensure(ls->getLength() != 0.0);
}

// Test of incomplete LineString user's constructor throwing the IllegalArgumentException
template<>
template<>
void object::test<3>
()
{
    // Single-element sequence of coordiantes
    CoordArrayPtr pseq = nullptr;
    try {
        pseq = new geos::geom::CoordinateArraySequence();
        ensure("sequence is null pointer.", pseq != nullptr);
        pseq->add(geos::geom::Coordinate(0, 0, 0));
        ensure_equals(pseq->size(), 1u);

        // Create incomplete linstring
        LineStringAutoPtr ls(factory_->createLineString(pseq));
        fail("IllegalArgumentException expected.");
    }
    catch(geos::util::IllegalArgumentException const& e) {
        // TODO - mloskot - is waiting for "exception-safety" and bugs resolution
        // If removed, memory 3 leaks occur
        //delete pseq;

        const char* msg = e.what(); // OK
        ensure(msg != nullptr);
    }
}

// Test of copy constructor
template<>
template<>
void object::test<4>
()
{
    using geos::geom::Coordinate;

    // Non-empty sequence of coordiantes
    const size_t size = 3;

    CoordArrayPtr pseq = new geos::geom::CoordinateArraySequence();
    ensure("sequence is null pointer.", pseq != nullptr);

    pseq->add(Coordinate(0, 0, 0));
    pseq->add(Coordinate(5, 5, 5));
    pseq->add(Coordinate(10, 10, 10));

    ensure_equals(pseq->size(), size);

    // Create examplar of linstring instance
    LineStringAutoPtr examplar(factory_->createLineString(pseq));

    // Create copy
    LineStringAutoPtr copy(dynamic_cast<geos::geom::LineString*>(examplar->clone().release()));

    ensure(nullptr != copy.get());

    ensure(!copy->isEmpty());
    ensure(copy->isSimple());
    ensure(copy->isValid());

    ensure(!copy->isClosed());
    ensure(!copy->isRing());

    std::unique_ptr<geos::geom::Geometry> geo;
    geo = copy->getEnvelope();
    ensure(geo != nullptr);
    ensure(!geo->isEmpty());

    geo = copy->getBoundary();
    ensure(geo != nullptr);
    ensure(!geo->isEmpty());

    geo = copy->convexHull();
    ensure(geo != nullptr);
    ensure(!geo->isEmpty());

    ensure_equals(copy->getGeometryTypeId(), geos::geom::GEOS_LINESTRING);
    ensure_equals(copy->getDimension(), geos::geom::Dimension::L);
    ensure_equals(copy->getBoundaryDimension(), geos::geom::Dimension::P);
    ensure_equals(copy->getNumPoints(), size);
    ensure_equals(copy->getArea(), 0.0);
    ensure(copy->getLength() != 0.0);
}

// Test of isClosed() and isRing() for empty linestring
template<>
template<>
void object::test<5>
()
{
    ensure(!empty_line_->isClosed());
    ensure(!empty_line_->isRing());
}

// Test of getEnvelope() for empty linestring
template<>
template<>
void object::test<6>
()
{
    auto geo = empty_line_->getEnvelope();
    ensure(geo != nullptr);
    ensure(geo->isEmpty());
}

// Test of getBoundary() for empty linestring
template<>
template<>
void object::test<7>
()
{
    auto geo = empty_line_->getBoundary();
    ensure(geo != nullptr);
    ensure(geo->isEmpty());
}

// Test of convexHull() for empty linestring
template<>
template<>
void object::test<8>
()
{
    auto geo = empty_line_->convexHull();
    ensure(geo != nullptr);
    ensure(geo->isEmpty());
}

// Test of getGeometryTypeId() for empty linestring
template<>
template<>
void object::test<9>
()
{
    ensure_equals(empty_line_->getGeometryTypeId(), geos::geom::GEOS_LINESTRING);
}

// Test of getDimension() for empty linestring
template<>
template<>
void object::test<10>
()
{
    ensure_equals(empty_line_->getDimension(), geos::geom::Dimension::L);
}

// Test of getBoundaryDimension() for empty linestring
template<>
template<>
void object::test<11>
()
{
    ensure_equals(empty_line_->getBoundaryDimension(), geos::geom::Dimension::P);
}

// Test of getNumPoints() for empty linestring
template<>
template<>
void object::test<12>
()
{
    ensure_equals(empty_line_->getNumPoints(), 0u);
}

// Test of getLength() for empty linestring
template<>
template<>
void object::test<13>
()
{
    ensure_equals(empty_line_->getLength(), 0.0);
}

// Test of getArea() for empty linestring
template<>
template<>
void object::test<14>
()
{
    ensure_equals(empty_line_->getArea(), 0.0);
}

// Test of isClosed() and isRing() for non-empty linestring
template<>
template<>
void object::test<15>
()
{
    auto geo = reader_.read("LINESTRING (0 0, 5 5, 10 5, 10 10)");
    ensure(geo != nullptr);

    LineStringPtr line = dynamic_cast<LineStringPtr>(geo.get());
    ensure(line != nullptr);

    ensure(!line->isEmpty());
    ensure(!line->isClosed());
    ensure(!line->isRing());
    ensure(line->getCoordinateDimension() == 2);
}

// Test of getEnvelope() for non-empty linestring
template<>
template<>
void object::test<16>
()
{
    auto geo = reader_.read("LINESTRING (0 0, 5 5, 10 5, 10 10)");
    ensure(geo != nullptr);

    LineStringPtr line = dynamic_cast<LineStringPtr>(geo.get());
    ensure(line != nullptr);

    auto envelope = line->getEnvelope();
    ensure(envelope != nullptr);
    ensure(!envelope->isEmpty());
    ensure_equals(envelope->getDimension(), geos::geom::Dimension::A);
}

// Test of getBoundary() for non-empty linestring
template<>
template<>
void object::test<17>
()
{
    auto geo = reader_.read("LINESTRING (0 0, 5 5, 10 5, 10 10)");
    ensure(geo != nullptr);

    LineStringPtr line = dynamic_cast<LineStringPtr>(geo.get());
    ensure(line != nullptr);

    auto boundary = line->getBoundary();
    ensure(boundary != nullptr);
    ensure(!boundary->isEmpty());
    ensure_equals(boundary->getGeometryTypeId(), geos::geom::GEOS_MULTIPOINT);
    ensure_equals(boundary->getDimension(), geos::geom::Dimension::P);
}

// Test of convexHull() for non-empty linestring
template<>
template<>
void object::test<18>
()
{
    auto geo = reader_.read("LINESTRING (0 0, 5 5, 10 5, 10 10)");
    ensure(geo != nullptr);

    LineStringPtr line = dynamic_cast<LineStringPtr>(geo.get());
    ensure(line != nullptr);

    auto hull = line->convexHull();
    ensure(hull != nullptr);
    ensure(!hull->isEmpty());
    ensure_equals(hull->getGeometryTypeId(), geos::geom::GEOS_POLYGON);
    ensure_equals(hull->getDimension(), geos::geom::Dimension::A);
}

// Test of getGeometryTypeId() for non-empty linestring
template<>
template<>
void object::test<19>
()
{
    auto geo = reader_.read("LINESTRING (0 0, 5 5, 10 5, 10 10)");
    ensure(geo != nullptr);

    ensure_equals(geo->getGeometryTypeId(), geos::geom::GEOS_LINESTRING);
}

// Test of getDimension() for non-empty linestring
template<>
template<>
void object::test<20>
()
{
    auto geo = reader_.read("LINESTRING (0 0, 5 5, 10 5, 10 10)");
    ensure(geo != nullptr);

    ensure_equals(geo->getDimension(), geos::geom::Dimension::L);
}

// Test of getBoundaryDimension() for non-empty linestring
template<>
template<>
void object::test<21>
()
{
    auto geo = reader_.read("LINESTRING (0 0, 5 5, 10 5, 10 10)");
    ensure(geo != nullptr);

    ensure_equals(geo->getBoundaryDimension(), geos::geom::Dimension::P);
}

// Test of getNumPoints() for non-empty linestring
template<>
template<>
void object::test<22>
()
{
    const size_t size = 4;
    auto geo = reader_.read("LINESTRING (0 0, 5 5, 10 5, 10 10)");
    ensure(geo != nullptr);

    ensure_equals(geo->getNumPoints(), size);
}

// Test of getLength() for non-empty linestring
template<>
template<>
void object::test<23>
()
{
    const double tolerance = 0.0001;
    const double expected = 2 * 14.142135600000000;
    auto geo = reader_.read("LINESTRING (0 0, 10 10, 20 0)");
    ensure(geo != nullptr);

    ensure(geo->getLength() != 0.0);

    const double diff = std::fabs(geo->getLength() - expected);
    ensure(diff <= tolerance);
}

// Test of getArea() for non-empty linestring
template<>
template<>
void object::test<24>
()
{
    auto geo = reader_.read("LINESTRING (0 0, 10 10, 20 0)");
    ensure(geo != nullptr);

    ensure_equals(geo->getArea(), 0.0);
}

// Test of getGeometryType() for non-empty LineString
template<>
template<>
void object::test<25>
()
{
    auto geo = reader_.read("LINESTRING (0 0, 10 10, 20 0)");
    ensure(geo != nullptr);

    const std::string type("LineString");
    ensure_equals(geo->getGeometryType(), type);
}

template<>
template<>
void object::test<26>
()
{
    // getCoordinate() returns nullptr for empty geometry
    auto gf = geos::geom::GeometryFactory::create();
    std::unique_ptr<geos::geom::Geometry> g(gf->createLineString());

    ensure(g->getCoordinate() == nullptr);
}

// test isDimensionStrict for empty LineString
template<>
template<>
void object::test<27>
()
{
    ensure(empty_line_->isDimensionStrict(geos::geom::Dimension::L));
    ensure(!empty_line_->isDimensionStrict(geos::geom::Dimension::A));
}

// test isDimensionStrict for non-empty LineString
template<>
template<>
void object::test<28>
()
{
    std::unique_ptr<geos::geom::Geometry> geo(reader_.read("LINESTRING (0 0, 10 10, 20 0))"));

    ensure(geo->isDimensionStrict(geos::geom::Dimension::L));
    ensure(!geo->isDimensionStrict(geos::geom::Dimension::A));
}
} // namespace tut

<commit_msg>Fix comment typos<commit_after>//
// Test Suite for geos::geom::LineString class.

// tut
#include <tut/tut.hpp>
#include <utility.h>
// geos
#include <geos/geom/LineString.h>
#include <geos/geom/Coordinate.h>
#include <geos/geom/CoordinateArraySequence.h>
#include <geos/geom/Dimension.h>
#include <geos/geom/Geometry.h>
#include <geos/geom/GeometryFactory.h>
#include <geos/geom/PrecisionModel.h>
#include <geos/io/WKTReader.h>
#include <geos/util/GEOSException.h>
#include <geos/util/IllegalArgumentException.h>
// std
#include <string>
#include <cmath>
#include <cassert>

namespace tut {
//
// Test Group
//

// Common data used by tests
struct test_linestring_data {
    // Typedefs used as short names by test cases
    typedef std::unique_ptr<geos::geom::LineString> LineStringAutoPtr;

    geos::geom::PrecisionModel pm_;
    geos::geom::GeometryFactory::Ptr factory_;
    geos::io::WKTReader reader_;

    LineStringPtr empty_line_;

    test_linestring_data()
        : pm_(1000)
        , factory_(geos::geom::GeometryFactory::create(&pm_, 0))
        , reader_(factory_.get())
        , empty_line_(factory_->createLineString(new geos::geom::CoordinateArraySequence()))
    {
        assert(nullptr != empty_line_);
    }

    ~test_linestring_data()
    {
        factory_->destroyGeometry(empty_line_);
        empty_line_ = nullptr;
    }
};

typedef test_group<test_linestring_data> group;
typedef group::object object;

group test_linestring_group("geos::geom::LineString");

//
// Test Cases
//

// Test of empty LineString created with user's constructor
template<>
template<>
void object::test<1>
()
{
    using geos::geom::Coordinate;

    // Empty sequence of coordinates
    CoordArrayPtr pseq = new geos::geom::CoordinateArraySequence();
    ensure("sequence is null pointer.", pseq != nullptr);

    // Create empty linstring instance
    LineStringAutoPtr ls(factory_->createLineString(pseq));

    ensure(ls->isEmpty());
    ensure(ls->isSimple());
    ensure(ls->isValid());
}

// Test of non-empty LineString created with user's constructor
template<>
template<>
void object::test<2>
()
{
    using geos::geom::Coordinate;

    // Non-empty sequence of coordinates
    const size_t size3 = 3;

    CoordArrayPtr pseq = new geos::geom::CoordinateArraySequence();
    ensure("sequence is null pointer.", pseq != nullptr);

    pseq->add(Coordinate(0, 0, 0));
    pseq->add(Coordinate(5, 5, 5));
    pseq->add(Coordinate(10, 10, 10));

    ensure_equals(pseq->size(), size3);

    // Create non-empty LineString instance
    LineStringAutoPtr ls(factory_->createLineString(pseq));

    ensure(!ls->isEmpty());
    ensure(ls->isSimple());
    ensure(ls->isValid());

    ensure(!ls->isClosed());
    ensure(!ls->isRing());

    std::unique_ptr<geos::geom::Geometry> geo;
    geo = ls->getEnvelope();
    ensure(geo != nullptr);
    ensure(!geo->isEmpty());

    geo = ls->getBoundary();
    ensure(geo != nullptr);
    ensure(!geo->isEmpty());

    geo = ls->convexHull();
    ensure(geo != nullptr);
    ensure(!geo->isEmpty());

    ensure_equals(ls->getGeometryTypeId(), geos::geom::GEOS_LINESTRING);
    ensure_equals(ls->getDimension(), geos::geom::Dimension::L);
    ensure_equals(ls->getBoundaryDimension(), geos::geom::Dimension::P);
    ensure_equals(ls->getCoordinateDimension(), 3);
    ensure_equals(ls->getNumPoints(), size3);
    ensure_equals(ls->getArea(), 0.0);
    ensure(ls->getLength() != 0.0);
}

// Test of incomplete LineString user's constructor throwing the IllegalArgumentException
template<>
template<>
void object::test<3>
()
{
    // Single-element sequence of coordinates
    CoordArrayPtr pseq = nullptr;
    try {
        pseq = new geos::geom::CoordinateArraySequence();
        ensure("sequence is null pointer.", pseq != nullptr);
        pseq->add(geos::geom::Coordinate(0, 0, 0));
        ensure_equals(pseq->size(), 1u);

        // Create incomplete LineString
        LineStringAutoPtr ls(factory_->createLineString(pseq));
        fail("IllegalArgumentException expected.");
    }
    catch(geos::util::IllegalArgumentException const& e) {
        // TODO - mloskot - is waiting for "exception-safety" and bugs resolution
        // If removed, memory 3 leaks occur
        //delete pseq;

        const char* msg = e.what(); // OK
        ensure(msg != nullptr);
    }
}

// Test of copy constructor
template<>
template<>
void object::test<4>
()
{
    using geos::geom::Coordinate;

    // Non-empty sequence of coordinates
    const size_t size = 3;

    CoordArrayPtr pseq = new geos::geom::CoordinateArraySequence();
    ensure("sequence is null pointer.", pseq != nullptr);

    pseq->add(Coordinate(0, 0, 0));
    pseq->add(Coordinate(5, 5, 5));
    pseq->add(Coordinate(10, 10, 10));

    ensure_equals(pseq->size(), size);

    // Create example of LineString instance
    LineStringAutoPtr examplar(factory_->createLineString(pseq));

    // Create copy
    LineStringAutoPtr copy(dynamic_cast<geos::geom::LineString*>(examplar->clone().release()));

    ensure(nullptr != copy.get());

    ensure(!copy->isEmpty());
    ensure(copy->isSimple());
    ensure(copy->isValid());

    ensure(!copy->isClosed());
    ensure(!copy->isRing());

    std::unique_ptr<geos::geom::Geometry> geo;
    geo = copy->getEnvelope();
    ensure(geo != nullptr);
    ensure(!geo->isEmpty());

    geo = copy->getBoundary();
    ensure(geo != nullptr);
    ensure(!geo->isEmpty());

    geo = copy->convexHull();
    ensure(geo != nullptr);
    ensure(!geo->isEmpty());

    ensure_equals(copy->getGeometryTypeId(), geos::geom::GEOS_LINESTRING);
    ensure_equals(copy->getDimension(), geos::geom::Dimension::L);
    ensure_equals(copy->getBoundaryDimension(), geos::geom::Dimension::P);
    ensure_equals(copy->getNumPoints(), size);
    ensure_equals(copy->getArea(), 0.0);
    ensure(copy->getLength() != 0.0);
}

// Test of isClosed() and isRing() for empty linestring
template<>
template<>
void object::test<5>
()
{
    ensure(!empty_line_->isClosed());
    ensure(!empty_line_->isRing());
}

// Test of getEnvelope() for empty linestring
template<>
template<>
void object::test<6>
()
{
    auto geo = empty_line_->getEnvelope();
    ensure(geo != nullptr);
    ensure(geo->isEmpty());
}

// Test of getBoundary() for empty linestring
template<>
template<>
void object::test<7>
()
{
    auto geo = empty_line_->getBoundary();
    ensure(geo != nullptr);
    ensure(geo->isEmpty());
}

// Test of convexHull() for empty linestring
template<>
template<>
void object::test<8>
()
{
    auto geo = empty_line_->convexHull();
    ensure(geo != nullptr);
    ensure(geo->isEmpty());
}

// Test of getGeometryTypeId() for empty linestring
template<>
template<>
void object::test<9>
()
{
    ensure_equals(empty_line_->getGeometryTypeId(), geos::geom::GEOS_LINESTRING);
}

// Test of getDimension() for empty linestring
template<>
template<>
void object::test<10>
()
{
    ensure_equals(empty_line_->getDimension(), geos::geom::Dimension::L);
}

// Test of getBoundaryDimension() for empty linestring
template<>
template<>
void object::test<11>
()
{
    ensure_equals(empty_line_->getBoundaryDimension(), geos::geom::Dimension::P);
}

// Test of getNumPoints() for empty linestring
template<>
template<>
void object::test<12>
()
{
    ensure_equals(empty_line_->getNumPoints(), 0u);
}

// Test of getLength() for empty linestring
template<>
template<>
void object::test<13>
()
{
    ensure_equals(empty_line_->getLength(), 0.0);
}

// Test of getArea() for empty linestring
template<>
template<>
void object::test<14>
()
{
    ensure_equals(empty_line_->getArea(), 0.0);
}

// Test of isClosed() and isRing() for non-empty linestring
template<>
template<>
void object::test<15>
()
{
    auto geo = reader_.read("LINESTRING (0 0, 5 5, 10 5, 10 10)");
    ensure(geo != nullptr);

    LineStringPtr line = dynamic_cast<LineStringPtr>(geo.get());
    ensure(line != nullptr);

    ensure(!line->isEmpty());
    ensure(!line->isClosed());
    ensure(!line->isRing());
    ensure(line->getCoordinateDimension() == 2);
}

// Test of getEnvelope() for non-empty linestring
template<>
template<>
void object::test<16>
()
{
    auto geo = reader_.read("LINESTRING (0 0, 5 5, 10 5, 10 10)");
    ensure(geo != nullptr);

    LineStringPtr line = dynamic_cast<LineStringPtr>(geo.get());
    ensure(line != nullptr);

    auto envelope = line->getEnvelope();
    ensure(envelope != nullptr);
    ensure(!envelope->isEmpty());
    ensure_equals(envelope->getDimension(), geos::geom::Dimension::A);
}

// Test of getBoundary() for non-empty linestring
template<>
template<>
void object::test<17>
()
{
    auto geo = reader_.read("LINESTRING (0 0, 5 5, 10 5, 10 10)");
    ensure(geo != nullptr);

    LineStringPtr line = dynamic_cast<LineStringPtr>(geo.get());
    ensure(line != nullptr);

    auto boundary = line->getBoundary();
    ensure(boundary != nullptr);
    ensure(!boundary->isEmpty());
    ensure_equals(boundary->getGeometryTypeId(), geos::geom::GEOS_MULTIPOINT);
    ensure_equals(boundary->getDimension(), geos::geom::Dimension::P);
}

// Test of convexHull() for non-empty linestring
template<>
template<>
void object::test<18>
()
{
    auto geo = reader_.read("LINESTRING (0 0, 5 5, 10 5, 10 10)");
    ensure(geo != nullptr);

    LineStringPtr line = dynamic_cast<LineStringPtr>(geo.get());
    ensure(line != nullptr);

    auto hull = line->convexHull();
    ensure(hull != nullptr);
    ensure(!hull->isEmpty());
    ensure_equals(hull->getGeometryTypeId(), geos::geom::GEOS_POLYGON);
    ensure_equals(hull->getDimension(), geos::geom::Dimension::A);
}

// Test of getGeometryTypeId() for non-empty linestring
template<>
template<>
void object::test<19>
()
{
    auto geo = reader_.read("LINESTRING (0 0, 5 5, 10 5, 10 10)");
    ensure(geo != nullptr);

    ensure_equals(geo->getGeometryTypeId(), geos::geom::GEOS_LINESTRING);
}

// Test of getDimension() for non-empty linestring
template<>
template<>
void object::test<20>
()
{
    auto geo = reader_.read("LINESTRING (0 0, 5 5, 10 5, 10 10)");
    ensure(geo != nullptr);

    ensure_equals(geo->getDimension(), geos::geom::Dimension::L);
}

// Test of getBoundaryDimension() for non-empty linestring
template<>
template<>
void object::test<21>
()
{
    auto geo = reader_.read("LINESTRING (0 0, 5 5, 10 5, 10 10)");
    ensure(geo != nullptr);

    ensure_equals(geo->getBoundaryDimension(), geos::geom::Dimension::P);
}

// Test of getNumPoints() for non-empty linestring
template<>
template<>
void object::test<22>
()
{
    const size_t size = 4;
    auto geo = reader_.read("LINESTRING (0 0, 5 5, 10 5, 10 10)");
    ensure(geo != nullptr);

    ensure_equals(geo->getNumPoints(), size);
}

// Test of getLength() for non-empty linestring
template<>
template<>
void object::test<23>
()
{
    const double tolerance = 0.0001;
    const double expected = 2 * 14.142135600000000;
    auto geo = reader_.read("LINESTRING (0 0, 10 10, 20 0)");
    ensure(geo != nullptr);

    ensure(geo->getLength() != 0.0);

    const double diff = std::fabs(geo->getLength() - expected);
    ensure(diff <= tolerance);
}

// Test of getArea() for non-empty linestring
template<>
template<>
void object::test<24>
()
{
    auto geo = reader_.read("LINESTRING (0 0, 10 10, 20 0)");
    ensure(geo != nullptr);

    ensure_equals(geo->getArea(), 0.0);
}

// Test of getGeometryType() for non-empty LineString
template<>
template<>
void object::test<25>
()
{
    auto geo = reader_.read("LINESTRING (0 0, 10 10, 20 0)");
    ensure(geo != nullptr);

    const std::string type("LineString");
    ensure_equals(geo->getGeometryType(), type);
}

template<>
template<>
void object::test<26>
()
{
    // getCoordinate() returns nullptr for empty geometry
    auto gf = geos::geom::GeometryFactory::create();
    std::unique_ptr<geos::geom::Geometry> g(gf->createLineString());

    ensure(g->getCoordinate() == nullptr);
}

// test isDimensionStrict for empty LineString
template<>
template<>
void object::test<27>
()
{
    ensure(empty_line_->isDimensionStrict(geos::geom::Dimension::L));
    ensure(!empty_line_->isDimensionStrict(geos::geom::Dimension::A));
}

// test isDimensionStrict for non-empty LineString
template<>
template<>
void object::test<28>
()
{
    std::unique_ptr<geos::geom::Geometry> geo(reader_.read("LINESTRING (0 0, 10 10, 20 0))"));

    ensure(geo->isDimensionStrict(geos::geom::Dimension::L));
    ensure(!geo->isDimensionStrict(geos::geom::Dimension::A));
}
} // namespace tut

<|endoftext|>


<commit_before>#include "LLVM_Headers.h"
#include "LLVM_Output.h"
#include "LLVM_Runtime_Linker.h"
#include "CodeGen_LLVM.h"
#include "CodeGen_C.h"
#include "CodeGen_Internal.h"

#include <iostream>
#include <fstream>

namespace Halide {

std::unique_ptr<llvm::raw_fd_ostream> make_raw_fd_ostream(const std::string &filename) {
    std::string error_string;
    std::error_code err;
    std::unique_ptr<llvm::raw_fd_ostream> raw_out(new llvm::raw_fd_ostream(filename, err, llvm::sys::fs::F_None));
    if (err) error_string = err.message();
    internal_assert(error_string.empty())
        << "Error opening output " << filename << ": " << error_string << "\n";

    return raw_out;
}

void emit_file(llvm::Module &module, Internal::LLVMOStream& out, llvm::TargetMachine::CodeGenFileType file_type) {
    Internal::debug(1) << "emit_file.Compiling to native code...\n";
    Internal::debug(2) << "Target triple: " << module.getTargetTriple() << "\n";

    // Get the target specific parser.
    auto target_machine = Internal::make_target_machine(module);
    internal_assert(target_machine.get()) << "Could not allocate target machine!\n";

    #if LLVM_VERSION == 37
    llvm::DataLayout target_data_layout(*(target_machine->getDataLayout()));
    #else
    llvm::DataLayout target_data_layout(target_machine->createDataLayout());
    #endif
    if (!(target_data_layout == module.getDataLayout())) {
        internal_error << "Warning: module's data layout does not match target machine's\n"
                       << target_data_layout.getStringRepresentation() << "\n"
                       << module.getDataLayout().getStringRepresentation() << "\n";
    }

    // Build up all of the passes that we want to do to the module.
    llvm::legacy::PassManager pass_manager;

    pass_manager.add(new llvm::TargetLibraryInfoWrapperPass(llvm::Triple(module.getTargetTriple())));

    // Make sure things marked as always-inline get inlined
    #if LLVM_VERSION < 40
    pass_manager.add(llvm::createAlwaysInlinerPass());
    #else
    pass_manager.add(llvm::createAlwaysInlinerLegacyPass());
    #endif

    // Enable symbol rewriting. This allows code outside libHalide to
    // use symbol rewriting when compiling Halide code (for example, by
    // using cl::ParseCommandLineOption and then passing the appropriate
    // rewrite options via -mllvm flags).
    pass_manager.add(llvm::createRewriteSymbolsPass());

    // Override default to generate verbose assembly.
    target_machine->Options.MCOptions.AsmVerbose = true;

    // Ask the target to add backend passes as necessary.
    target_machine->addPassesToEmitFile(pass_manager, out, file_type);

    pass_manager.run(module);
}

std::unique_ptr<llvm::Module> compile_module_to_llvm_module(const Module &module, llvm::LLVMContext &context) {
    return codegen_llvm(module, context);
}

void compile_llvm_module_to_object(llvm::Module &module, Internal::LLVMOStream& out) {
    emit_file(module, out, llvm::TargetMachine::CGFT_ObjectFile);
}

void compile_llvm_module_to_assembly(llvm::Module &module, Internal::LLVMOStream& out) {
    emit_file(module, out, llvm::TargetMachine::CGFT_AssemblyFile);
}

void compile_llvm_module_to_llvm_bitcode(llvm::Module &module, Internal::LLVMOStream& out) {
    WriteBitcodeToFile(&module, out);
}

void compile_llvm_module_to_llvm_assembly(llvm::Module &module, Internal::LLVMOStream& out) {
    module.print(out, nullptr);
}

void create_static_library(const std::vector<std::string> &src_files, const Target &target,
                    const std::string &dst_file, bool deterministic) {
    internal_assert(!src_files.empty());
#if !defined(WITH_NATIVE_CLIENT)

#if LLVM_VERSION >= 39
    std::vector<llvm::NewArchiveMember> new_members;
    for (auto &src : src_files) {
        llvm::Expected<llvm::NewArchiveMember> new_member =
            llvm::NewArchiveMember::getFile(src, /*Deterministic=*/true);
        internal_assert((bool)new_member)
            << src << ": " << llvm::toString(new_member.takeError()) << "\n";
        new_members.push_back(std::move(*new_member));
    }
#elif LLVM_VERSION == 38
    std::vector<llvm::NewArchiveIterator> new_members;
    for (auto &src : src_files) {
        new_members.push_back(llvm::NewArchiveIterator(src));
    }
#else
    std::vector<llvm::NewArchiveIterator> new_members;
    for (auto &src : src_files) {
        new_members.push_back(llvm::NewArchiveIterator(src, src));
    }
#endif

    const bool write_symtab = true;
    const auto kind = Internal::get_triple_for_target(target).isOSDarwin()
        ? llvm::object::Archive::K_BSD
        : llvm::object::Archive::K_GNU;
#if LLVM_VERSION == 37
    auto result = llvm::writeArchive(dst_file, new_members,
                       write_symtab, kind,
                       deterministic);
#elif LLVM_VERSION == 38
    const bool thin = false;
    auto result = llvm::writeArchive(dst_file, new_members,
                       write_symtab, kind,
                       deterministic, thin);
#else
    const bool thin = false;
    auto result = llvm::writeArchive(dst_file, new_members,
                       write_symtab, kind,
                       deterministic, thin, nullptr);
#endif
    internal_assert(!result.second) << "Failed to write archive: " << dst_file
        << ", reason: " << result.second << "\n";
#else  // <= 3.6 or PNacl
    // LLVM 3.6-and-earlier don't expose the right API. Halide no longer officially
    // supports these versions, but we still do some build-and-testing on them
    // as a way to improve pnacl-llvm coverage (since it's somewhere between 3.6 and 3.7),
    // so as a stopgap measure to allow Makefiles to rely on static_library output,
    // shell out to 'ar' to do the work. This will hopefully be short-lived as even
    // limited support for pre-3.7 shouldn't need to live much longer.
    // (Also note that we don't use the "deterministic" flag since not all ar implementations
    // support it.)
    std::string command = "ar qsv " + dst_file;
    for (auto &src : src_files) {
        command += " " + src;
    }
    int result = system(command.c_str());
    internal_assert(result == 0) << "shelling out to ar failed.\n";
#endif
}

}  // namespace Halide
<commit_msg>Fix latent bug in create_static_library()<commit_after>#include "LLVM_Headers.h"
#include "LLVM_Output.h"
#include "LLVM_Runtime_Linker.h"
#include "CodeGen_LLVM.h"
#include "CodeGen_C.h"
#include "CodeGen_Internal.h"

#include <iostream>
#include <fstream>

namespace Halide {

std::unique_ptr<llvm::raw_fd_ostream> make_raw_fd_ostream(const std::string &filename) {
    std::string error_string;
    std::error_code err;
    std::unique_ptr<llvm::raw_fd_ostream> raw_out(new llvm::raw_fd_ostream(filename, err, llvm::sys::fs::F_None));
    if (err) error_string = err.message();
    internal_assert(error_string.empty())
        << "Error opening output " << filename << ": " << error_string << "\n";

    return raw_out;
}

void emit_file(llvm::Module &module, Internal::LLVMOStream& out, llvm::TargetMachine::CodeGenFileType file_type) {
    Internal::debug(1) << "emit_file.Compiling to native code...\n";
    Internal::debug(2) << "Target triple: " << module.getTargetTriple() << "\n";

    // Get the target specific parser.
    auto target_machine = Internal::make_target_machine(module);
    internal_assert(target_machine.get()) << "Could not allocate target machine!\n";

    #if LLVM_VERSION == 37
    llvm::DataLayout target_data_layout(*(target_machine->getDataLayout()));
    #else
    llvm::DataLayout target_data_layout(target_machine->createDataLayout());
    #endif
    if (!(target_data_layout == module.getDataLayout())) {
        internal_error << "Warning: module's data layout does not match target machine's\n"
                       << target_data_layout.getStringRepresentation() << "\n"
                       << module.getDataLayout().getStringRepresentation() << "\n";
    }

    // Build up all of the passes that we want to do to the module.
    llvm::legacy::PassManager pass_manager;

    pass_manager.add(new llvm::TargetLibraryInfoWrapperPass(llvm::Triple(module.getTargetTriple())));

    // Make sure things marked as always-inline get inlined
    #if LLVM_VERSION < 40
    pass_manager.add(llvm::createAlwaysInlinerPass());
    #else
    pass_manager.add(llvm::createAlwaysInlinerLegacyPass());
    #endif

    // Enable symbol rewriting. This allows code outside libHalide to
    // use symbol rewriting when compiling Halide code (for example, by
    // using cl::ParseCommandLineOption and then passing the appropriate
    // rewrite options via -mllvm flags).
    pass_manager.add(llvm::createRewriteSymbolsPass());

    // Override default to generate verbose assembly.
    target_machine->Options.MCOptions.AsmVerbose = true;

    // Ask the target to add backend passes as necessary.
    target_machine->addPassesToEmitFile(pass_manager, out, file_type);

    pass_manager.run(module);
}

std::unique_ptr<llvm::Module> compile_module_to_llvm_module(const Module &module, llvm::LLVMContext &context) {
    return codegen_llvm(module, context);
}

void compile_llvm_module_to_object(llvm::Module &module, Internal::LLVMOStream& out) {
    emit_file(module, out, llvm::TargetMachine::CGFT_ObjectFile);
}

void compile_llvm_module_to_assembly(llvm::Module &module, Internal::LLVMOStream& out) {
    emit_file(module, out, llvm::TargetMachine::CGFT_AssemblyFile);
}

void compile_llvm_module_to_llvm_bitcode(llvm::Module &module, Internal::LLVMOStream& out) {
    WriteBitcodeToFile(&module, out);
}

void compile_llvm_module_to_llvm_assembly(llvm::Module &module, Internal::LLVMOStream& out) {
    module.print(out, nullptr);
}

void create_static_library(const std::vector<std::string> &src_files, const Target &target,
                    const std::string &dst_file, bool deterministic) {
    internal_assert(!src_files.empty());
#if !defined(WITH_NATIVE_CLIENT)

#if LLVM_VERSION >= 39
    std::vector<llvm::NewArchiveMember> new_members;
    for (auto &src : src_files) {
        llvm::Expected<llvm::NewArchiveMember> new_member =
            llvm::NewArchiveMember::getFile(src, /*Deterministic=*/true);
        if (!new_member) {
            // Don't use internal_assert: the call to new_member.takeError() will be evaluated
            // even if the assert does not fail, leaving new_member in an indeterminate
            // state.
            internal_error << src << ": " << llvm::toString(new_member.takeError()) << "\n";
        }
        new_members.push_back(std::move(*new_member));
    }
#elif LLVM_VERSION == 38
    std::vector<llvm::NewArchiveIterator> new_members;
    for (auto &src : src_files) {
        new_members.push_back(llvm::NewArchiveIterator(src));
    }
#else
    std::vector<llvm::NewArchiveIterator> new_members;
    for (auto &src : src_files) {
        new_members.push_back(llvm::NewArchiveIterator(src, src));
    }
#endif

    const bool write_symtab = true;
    const auto kind = Internal::get_triple_for_target(target).isOSDarwin()
        ? llvm::object::Archive::K_BSD
        : llvm::object::Archive::K_GNU;
#if LLVM_VERSION == 37
    auto result = llvm::writeArchive(dst_file, new_members,
                       write_symtab, kind,
                       deterministic);
#elif LLVM_VERSION == 38
    const bool thin = false;
    auto result = llvm::writeArchive(dst_file, new_members,
                       write_symtab, kind,
                       deterministic, thin);
#else
    const bool thin = false;
    auto result = llvm::writeArchive(dst_file, new_members,
                       write_symtab, kind,
                       deterministic, thin, nullptr);
#endif
    internal_assert(!result.second) << "Failed to write archive: " << dst_file
        << ", reason: " << result.second << "\n";
#else  // <= 3.6 or PNacl
    // LLVM 3.6-and-earlier don't expose the right API. Halide no longer officially
    // supports these versions, but we still do some build-and-testing on them
    // as a way to improve pnacl-llvm coverage (since it's somewhere between 3.6 and 3.7),
    // so as a stopgap measure to allow Makefiles to rely on static_library output,
    // shell out to 'ar' to do the work. This will hopefully be short-lived as even
    // limited support for pre-3.7 shouldn't need to live much longer.
    // (Also note that we don't use the "deterministic" flag since not all ar implementations
    // support it.)
    std::string command = "ar qsv " + dst_file;
    for (auto &src : src_files) {
        command += " " + src;
    }
    int result = system(command.c_str());
    internal_assert(result == 0) << "shelling out to ar failed.\n";
#endif
}

}  // namespace Halide
<|endoftext|>