<commit_before>//===- llvm/unittest/Support/DynamicLibrary/PipSqueak.cxx -----------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "PipSqueak.h"
#include <string>

struct Global {
  std::string *Str;
  Global() : Str(nullptr) {}
  ~Global() {
    if (Str)
      *Str = "Global::~Global";
  }
};

struct Local {
  std::string &Str;
  Local(std::string &S) : Str(S) { Str = "Local::Local"; }
  ~Local() { Str = "Local::~Local"; }
};

static Global Glb;

extern "C" PIPSQUEAK_EXPORT void SetStrings(std::string &GStr,
                                            std::string &LStr) {
  static Local Lcl(LStr);
  Glb.Str = &GStr;
}

extern "C" PIPSQUEAK_EXPORT const char *TestA() { return "LibCall"; }
<commit_msg>Fix warnings from test added in r301562 on Windows (when built without exceptions).<commit_after>//===- llvm/unittest/Support/DynamicLibrary/PipSqueak.cxx -----------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "PipSqueak.h"

#ifdef _WIN32
// Disable warnings from inclusion of xlocale & exception
#pragma warning(push)
#pragma warning(disable: 4530)
#pragma warning(disable: 4577)
#include <string>
#pragma warning(pop)
#else
#include <string>
#endif

struct Global {
  std::string *Str;
  Global() : Str(nullptr) {}
  ~Global() {
    if (Str)
      *Str = "Global::~Global";
  }
};

struct Local {
  std::string &Str;
  Local(std::string &S) : Str(S) { Str = "Local::Local"; }
  ~Local() { Str = "Local::~Local"; }
};

static Global Glb;

extern "C" PIPSQUEAK_EXPORT void SetStrings(std::string &GStr,
                                            std::string &LStr) {
  static Local Lcl(LStr);
  Glb.Str = &GStr;
}

extern "C" PIPSQUEAK_EXPORT const char *TestA() { return "LibCall"; }
<|endoftext|>


<commit_before>/*
Copyright c1997-2013 Trygve Isaacson. All rights reserved.
This file is part of the Code Vault version 4.0
http://www.bombaydigital.com/
*/

/** @file */

#include "vfsnode.h"
#include "vtypes_internal.h"

#include "vexception.h"
#include "vthread.h"
#ifdef VCOMPILER_MSVC
    #pragma warning(disable: 6387)  // the library file doesn't past muster
#endif
#include <shlobj.h>
#ifdef VCOMPILER_MSVC
    #pragma warning(default: 6387)
#endif

/* OS APIs called, due for wrapping:

_wgetcwd
SHGetFolderPathW
mkdir
GetModuleFileNameW
wrap_stat
GetFileAttributesW
wrap_mkdir
wrap_rmdir
wrap_unlink
wrap_rename
FindFirstFileW
FindNextFileW
FindClose

I think I will redefine VFileSystemAPI to use VString parameters instead of char*.
Then take the vault namespace low level OS wrapper functions and do the same.
Then the Win32 versions of those can do the last-moment wide string conversion.
This keeps the rest of the code VString-centric so it doesn't expose the conversion junk.

*/

/*
Notes on use of Win32 "wide" APIs:

Win32 has three flavors of each string-based API.
The single-byte char, or "ANSI" versions, have an "A" suffix.
  For example: FindFirstFileA(const char* path, WIN32_FIND_DATAA* data)
  (WIN32_FIND_DATAA has instance variables containing char)
  These use ANSI C char strings that must be in the "current" code page.
The two-byte char, or "wide" versions, have a "W" suffix.
  For example: FindFirstFileW(const wchar_t* path, WIN32_FIND_DATAW* data)
  (WIN32_FIND_DATAW has instance variables containing wchar_t)
  These use UTF-16 wchar_t strings, so they take Unicode in UTF-16 format.
A version of each API without any suffix at all is macro-based and defined
as the "A" version if UNICODE is not defined, or the "W" version if UNICODE is defined.
  For example: FindFirstFile() is defined as either FindFirstFileA() or FindFirstFileW()

We call the "W" versions directly, since our paths are all VString objects internally,
which store Unicode in UTF-8 format, and we can easily convert to/from UTF-16 when we call
the Win32 APIs here.
For APIs where we pass the string, we just convert on-the-fly as the parameter:
  SetterW(s.toUTF16().c_str());
For APIs where we get a string back, we pass a wchar_t buffer and then construct a
VString from it afterwards:
  wchar_t buf[LENGTH];
  GetterW(buf);
  VString s(buf);
*/

/* Note: according to Microsoft KB article 177506, only the following characters
are valid in file and folder names on their operating system. I should provide
some way of filtering out bad stuff for the particular platform.

- letters
- numbers
^   Accent circumflex (caret)
&   Ampersand
'   Apostrophe (single quotation mark)
@   At sign
{   Brace left
}   Brace right
[   Bracket opening
]   Bracket closing
,   Comma
$   Dollar sign
=   Equal sign
!   Exclamation point
-   Hyphen
#   Number sign
(   Parenthesis opening
)   Parenthesis closing
%   Percent
.   Period
+   Plus
~   Tilde
_   Underscore
*/

// static
void VFSNode::_platform_normalizePath(VString& path) {
    // For the moment, we get almost all the functionality we need by
    // simply converting backslash to slash if we are compiled for Windows.
    // Mac OS X support is free since it's Unix. Mac OS 9 support would
    // have to deal with ':'. DOS drive letters and Mac OS 9 root/relative
    // paths would complicate things a little for things like getParentPath.
    path.replace("\\", "/");
}

// static
void VFSNode::_platform_denormalizePath(VString& path) {
    // See comments above.
    path.replace("/", "\\");
}

// static
VFSNode VFSNode::_platform_getKnownDirectoryNode(KnownDirectoryIdentifier id, const VString& companyName, const VString& appName) {
    if (id == CURRENT_WORKING_DIRECTORY) {
        wchar_t cwdPath[MAX_PATH];
        wchar_t* result = _wgetcwd(cwdPath, sizeof(cwdPath));
        if (result == NULL) {
            throw VStackTraceException(VSystemError(), "VFSNode::_platform_getKnownDirectoryNode: _wgetcwd failed.");
        }

        VString cwdPathString(cwdPath);
        VFSNode::normalizePath(cwdPathString);
        VFSNode cwdNode(cwdPathString);
        return cwdNode;
    }

    if (id == EXECUTABLE_DIRECTORY) {
        VFSNode executable = VFSNode::getExecutable();
        VFSNode executableDirectory;
        executable.getParentNode(executableDirectory);
        return executableDirectory;
    }

    wchar_t pathBuffer[MAX_PATH];
    HRESULT result = ::SHGetFolderPathW(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, pathBuffer);

    if (result != S_OK) {
        throw VStackTraceException(VSTRING_FORMAT("VFSNode::_platform_getKnownDirectoryNode: Unable to find current user Application Data folder. Error code %d.", (int) result));
    }

    VString path(pathBuffer);
    VFSNode::_platform_normalizePath(path);
    VFSNode appDataFolder(path);

    // User's folder is one level up from user's Application Data folder.
    VFSNode currentUserFolder;
    appDataFolder.getParentNode(currentUserFolder);

    if (id == USER_HOME_DIRECTORY) {
        return currentUserFolder;
    }

    VFSNode companyFolder;
    if (companyName.isEmpty()) {
        companyFolder = appDataFolder;
    } else {
        appDataFolder.getChildNode(companyName, companyFolder);
        companyFolder.mkdir();
    }

    VFSNode appFolder;
    if (appName.isEmpty()) {
        appFolder = companyFolder;
    } else {
        companyFolder.getChildNode(appName, appFolder);
        appFolder.mkdir();
    }

    VFSNode resultNode;

    switch (id) {
        case USER_HOME_DIRECTORY:
            // handled earlier; we returned above
            break;

        case LOG_FILES_DIRECTORY:
            appFolder.getChildNode("Logs", resultNode);
            break;

        case USER_PREFERENCES_DIRECTORY:
            appFolder.getChildNode("Preferences", resultNode);
            break;

        case CACHED_DATA_DIRECTORY:
            appFolder.getChildNode("Caches", resultNode);
            break;

        case APPLICATION_DATA_DIRECTORY:
            resultNode = appFolder;
            break;

        case EXECUTABLE_DIRECTORY:
            // handled earlier; we returned above
            break;

        default:
            throw VStackTraceException(VSTRING_FORMAT("VFSNode::_platform_getKnownDirectoryNode: Requested invalid directory ID %d.", (int) id));
            break;
    }

    resultNode.mkdir();

    return resultNode;
}

// static
VFSNode VFSNode::_platform_getExecutable() {
    wchar_t exePathBuffer[MAX_PATH];
    DWORD result = ::GetModuleFileNameW(HMODULE(NULL), exePathBuffer, DWORD(MAX_PATH));

    if (result == 0)
        throw VStackTraceException(VSystemError(), "VFSNode::_platform_getExecutable: Unable to determine exe path.");

    VString exePath(exePathBuffer);
    VFSNode::normalizePath(exePath); // must supply normalized form to VFSNode below
    VFSNode exeNode(exePath);
    return exeNode;
}

bool VFSNode::_platform_getNodeInfo(VFSNodeInfo& info) const {
    struct stat statData;
    int result = VFileSystemAPI::wrap_stat(mPath, &statData);

    if (result >= 0) {
        info.mCreationDate = CONST_S64(1000) * static_cast<Vs64>(statData.st_ctime);
        info.mModificationDate = CONST_S64(1000) * static_cast<Vs64>(statData.st_mtime);
        info.mFileSize = statData.st_size;
        DWORD attributes = ::GetFileAttributesW(mPath.toUTF16().c_str());
        info.mIsFile = ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0);
        info.mIsDirectory = ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
        info.mErrNo = 0;
    } else {
        info.mErrNo = errno;
    }

    return (result >= 0);
}

void VFSNode::_platform_createDirectory() const {
    int result = VFileSystemAPI::wrap_mkdir(mPath, (S_IFDIR | S_IRWXO | S_IRWXG | S_IRWXU));

    if (result != 0)
        throw VException(VSystemError(), VSTRING_FORMAT("VFSNode::_platform_createDirectory failed with result %d for '%s'.", result, mPath.chars()));
}

bool VFSNode::_platform_removeDirectory() const {
    int result = VFileSystemAPI::wrap_rmdir(mPath);
    return (result == 0);
}

bool VFSNode::_platform_removeFile() const {
    int result = VFileSystemAPI::wrap_unlink(mPath);
    return (result == 0);
}

void VFSNode::_platform_renameNode(const VString& newPath) const {
    int result = VFileSystemAPI::wrap_rename(mPath, newPath);

    if (result != 0)
        throw VException(VSystemError(), VSTRING_FORMAT("VFSNode::_platform_renameNode failed with result %d renaming '%s' to '%s'.", result, mPath.chars(), newPath.chars()));
}

// This is the Windows implementation of directory iteration using
// FindFirstFile(), FindNextFile(), FindClose() functions.

void VFSNode::_platform_directoryIterate(VDirectoryIterationCallback& callback) const {
    VString nodeName;
    VString searchPath(VSTRING_ARGS("%s/*", mPath.chars()));

    VFSNode::denormalizePath(searchPath);    // make it have DOS syntax

    WIN32_FIND_DATAW data;
    HANDLE dir = ::FindFirstFileW(searchPath.toUTF16().c_str(), &data);

    if (dir == INVALID_HANDLE_VALUE) {
        DWORD error = ::GetLastError();

        if (error == ERROR_NO_MORE_FILES) {
            return;
        }

        throw VException(VSTRING_FORMAT("VFSNode::_platform_getDirectoryList failed (error %d) for directory '%s'.", error , searchPath.chars()));
    }

    try {
        bool keepGoing = true;

        do {
            VThread::yield(); // be nice if we're iterating over a huge directory

            nodeName = data.cFileName; // assign VString from wide char string

            // Skip current and parent pseudo-entries. Otherwise client must
            // know too much detail in order to avoid traversal problems.
            if ((nodeName != ".") &&
                    (nodeName != "..")) {
                VFSNode childNode;
                this->getChildNode(nodeName, childNode);
                keepGoing = callback.handleNextNode(childNode);
            }

        } while (keepGoing && ::FindNextFileW(dir, &data));
    } catch (...) {
        ::FindClose(dir);
        throw;
    }

    ::FindClose(dir);
}

<commit_msg>VAULT-39 [trygve] Fix bug in Win32 getcwd call.<commit_after>/*
Copyright c1997-2013 Trygve Isaacson. All rights reserved.
This file is part of the Code Vault version 4.0
http://www.bombaydigital.com/
*/

/** @file */

#include "vfsnode.h"
#include "vtypes_internal.h"

#include "vexception.h"
#include "vthread.h"
#ifdef VCOMPILER_MSVC
    #pragma warning(disable: 6387)  // the library file doesn't past muster
#endif
#include <shlobj.h>
#ifdef VCOMPILER_MSVC
    #pragma warning(default: 6387)
#endif

/* OS APIs called, due for wrapping:

_wgetcwd
SHGetFolderPathW
mkdir
GetModuleFileNameW
wrap_stat
GetFileAttributesW
wrap_mkdir
wrap_rmdir
wrap_unlink
wrap_rename
FindFirstFileW
FindNextFileW
FindClose

I think I will redefine VFileSystemAPI to use VString parameters instead of char*.
Then take the vault namespace low level OS wrapper functions and do the same.
Then the Win32 versions of those can do the last-moment wide string conversion.
This keeps the rest of the code VString-centric so it doesn't expose the conversion junk.

*/

/*
Notes on use of Win32 "wide" APIs:

Win32 has three flavors of each string-based API.
The single-byte char, or "ANSI" versions, have an "A" suffix.
  For example: FindFirstFileA(const char* path, WIN32_FIND_DATAA* data)
  (WIN32_FIND_DATAA has instance variables containing char)
  These use ANSI C char strings that must be in the "current" code page.
The two-byte char, or "wide" versions, have a "W" suffix.
  For example: FindFirstFileW(const wchar_t* path, WIN32_FIND_DATAW* data)
  (WIN32_FIND_DATAW has instance variables containing wchar_t)
  These use UTF-16 wchar_t strings, so they take Unicode in UTF-16 format.
A version of each API without any suffix at all is macro-based and defined
as the "A" version if UNICODE is not defined, or the "W" version if UNICODE is defined.
  For example: FindFirstFile() is defined as either FindFirstFileA() or FindFirstFileW()

We call the "W" versions directly, since our paths are all VString objects internally,
which store Unicode in UTF-8 format, and we can easily convert to/from UTF-16 when we call
the Win32 APIs here.
For APIs where we pass the string, we just convert on-the-fly as the parameter:
  SetterW(s.toUTF16().c_str());
For APIs where we get a string back, we pass a wchar_t buffer and then construct a
VString from it afterwards:
  wchar_t buf[LENGTH];
  GetterW(buf);
  VString s(buf);
*/

/* Note: according to Microsoft KB article 177506, only the following characters
are valid in file and folder names on their operating system. I should provide
some way of filtering out bad stuff for the particular platform.

- letters
- numbers
^   Accent circumflex (caret)
&   Ampersand
'   Apostrophe (single quotation mark)
@   At sign
{   Brace left
}   Brace right
[   Bracket opening
]   Bracket closing
,   Comma
$   Dollar sign
=   Equal sign
!   Exclamation point
-   Hyphen
#   Number sign
(   Parenthesis opening
)   Parenthesis closing
%   Percent
.   Period
+   Plus
~   Tilde
_   Underscore
*/

// static
void VFSNode::_platform_normalizePath(VString& path) {
    // For the moment, we get almost all the functionality we need by
    // simply converting backslash to slash if we are compiled for Windows.
    // Mac OS X support is free since it's Unix. Mac OS 9 support would
    // have to deal with ':'. DOS drive letters and Mac OS 9 root/relative
    // paths would complicate things a little for things like getParentPath.
    path.replace("\\", "/");
}

// static
void VFSNode::_platform_denormalizePath(VString& path) {
    // See comments above.
    path.replace("/", "\\");
}

// static
VFSNode VFSNode::_platform_getKnownDirectoryNode(KnownDirectoryIdentifier id, const VString& companyName, const VString& appName) {
    if (id == CURRENT_WORKING_DIRECTORY) {
        wchar_t cwdPath[MAX_PATH];
        wchar_t* cwdResult = _wgetcwd(cwdPath, MAX_PATH);
        if (cwdResult == NULL) {
            throw VStackTraceException(VSystemError(), "VFSNode::_platform_getKnownDirectoryNode: _wgetcwd failed.");
        }

        VString cwdPathString(cwdPath);
        VFSNode::normalizePath(cwdPathString);
        VFSNode cwdNode(cwdPathString);
        return cwdNode;
    }

    if (id == EXECUTABLE_DIRECTORY) {
        VFSNode executable = VFSNode::getExecutable();
        VFSNode executableDirectory;
        executable.getParentNode(executableDirectory);
        return executableDirectory;
    }

    wchar_t pathBuffer[MAX_PATH];
    HRESULT gfpResult = ::SHGetFolderPathW(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, pathBuffer);

    if (gfpResult != S_OK) {
        throw VStackTraceException(VSTRING_FORMAT("VFSNode::_platform_getKnownDirectoryNode: Unable to find current user Application Data folder. Error code %d.", (int) gfpResult));
    }

    VString path(pathBuffer);
    VFSNode::_platform_normalizePath(path);
    VFSNode appDataFolder(path);

    // User's folder is one level up from user's Application Data folder.
    VFSNode currentUserFolder;
    appDataFolder.getParentNode(currentUserFolder);

    if (id == USER_HOME_DIRECTORY) {
        return currentUserFolder;
    }

    VFSNode companyFolder;
    if (companyName.isEmpty()) {
        companyFolder = appDataFolder;
    } else {
        appDataFolder.getChildNode(companyName, companyFolder);
        companyFolder.mkdir();
    }

    VFSNode appFolder;
    if (appName.isEmpty()) {
        appFolder = companyFolder;
    } else {
        companyFolder.getChildNode(appName, appFolder);
        appFolder.mkdir();
    }

    VFSNode resultNode;

    switch (id) {
        case USER_HOME_DIRECTORY:
            // handled earlier; we returned above
            break;

        case LOG_FILES_DIRECTORY:
            appFolder.getChildNode("Logs", resultNode);
            break;

        case USER_PREFERENCES_DIRECTORY:
            appFolder.getChildNode("Preferences", resultNode);
            break;

        case CACHED_DATA_DIRECTORY:
            appFolder.getChildNode("Caches", resultNode);
            break;

        case APPLICATION_DATA_DIRECTORY:
            resultNode = appFolder;
            break;

        case EXECUTABLE_DIRECTORY:
            // handled earlier; we returned above
            break;

        default:
            throw VStackTraceException(VSTRING_FORMAT("VFSNode::_platform_getKnownDirectoryNode: Requested invalid directory ID %d.", (int) id));
            break;
    }

    resultNode.mkdir();

    return resultNode;
}

// static
VFSNode VFSNode::_platform_getExecutable() {
    wchar_t exePathBuffer[MAX_PATH];
    DWORD result = ::GetModuleFileNameW(HMODULE(NULL), exePathBuffer, DWORD(MAX_PATH));

    if (result == 0)
        throw VStackTraceException(VSystemError(), "VFSNode::_platform_getExecutable: Unable to determine exe path.");

    VString exePath(exePathBuffer);
    VFSNode::normalizePath(exePath); // must supply normalized form to VFSNode below
    VFSNode exeNode(exePath);
    return exeNode;
}

bool VFSNode::_platform_getNodeInfo(VFSNodeInfo& info) const {
    struct stat statData;
    int result = VFileSystemAPI::wrap_stat(mPath, &statData);

    if (result >= 0) {
        info.mCreationDate = CONST_S64(1000) * static_cast<Vs64>(statData.st_ctime);
        info.mModificationDate = CONST_S64(1000) * static_cast<Vs64>(statData.st_mtime);
        info.mFileSize = statData.st_size;
        DWORD attributes = ::GetFileAttributesW(mPath.toUTF16().c_str());
        info.mIsFile = ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0);
        info.mIsDirectory = ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
        info.mErrNo = 0;
    } else {
        info.mErrNo = errno;
    }

    return (result >= 0);
}

void VFSNode::_platform_createDirectory() const {
    int result = VFileSystemAPI::wrap_mkdir(mPath, (S_IFDIR | S_IRWXO | S_IRWXG | S_IRWXU));

    if (result != 0)
        throw VException(VSystemError(), VSTRING_FORMAT("VFSNode::_platform_createDirectory failed with result %d for '%s'.", result, mPath.chars()));
}

bool VFSNode::_platform_removeDirectory() const {
    int result = VFileSystemAPI::wrap_rmdir(mPath);
    return (result == 0);
}

bool VFSNode::_platform_removeFile() const {
    int result = VFileSystemAPI::wrap_unlink(mPath);
    return (result == 0);
}

void VFSNode::_platform_renameNode(const VString& newPath) const {
    int result = VFileSystemAPI::wrap_rename(mPath, newPath);

    if (result != 0)
        throw VException(VSystemError(), VSTRING_FORMAT("VFSNode::_platform_renameNode failed with result %d renaming '%s' to '%s'.", result, mPath.chars(), newPath.chars()));
}

// This is the Windows implementation of directory iteration using
// FindFirstFile(), FindNextFile(), FindClose() functions.

void VFSNode::_platform_directoryIterate(VDirectoryIterationCallback& callback) const {
    VString nodeName;
    VString searchPath(VSTRING_ARGS("%s/*", mPath.chars()));

    VFSNode::denormalizePath(searchPath);    // make it have DOS syntax

    WIN32_FIND_DATAW data;
    HANDLE dir = ::FindFirstFileW(searchPath.toUTF16().c_str(), &data);

    if (dir == INVALID_HANDLE_VALUE) {
        DWORD error = ::GetLastError();

        if (error == ERROR_NO_MORE_FILES) {
            return;
        }

        throw VException(VSTRING_FORMAT("VFSNode::_platform_getDirectoryList failed (error %d) for directory '%s'.", error , searchPath.chars()));
    }

    try {
        bool keepGoing = true;

        do {
            VThread::yield(); // be nice if we're iterating over a huge directory

            nodeName = data.cFileName; // assign VString from wide char string

            // Skip current and parent pseudo-entries. Otherwise client must
            // know too much detail in order to avoid traversal problems.
            if ((nodeName != ".") &&
                    (nodeName != "..")) {
                VFSNode childNode;
                this->getChildNode(nodeName, childNode);
                keepGoing = callback.handleNextNode(childNode);
            }

        } while (keepGoing && ::FindNextFileW(dir, &data));
    } catch (...) {
        ::FindClose(dir);
        throw;
    }

    ::FindClose(dir);
}

<|endoftext|>


<commit_before>/*
   (c) Copyright 2012-2013  DirectFB integrated media GmbH
   (c) Copyright 2001-2013  The world wide DirectFB Open Source Community (directfb.org)
   (c) Copyright 2000-2004  Convergence (integrated media) GmbH

   All rights reserved.

   Written by Denis Oliver Kropp <dok@directfb.org>,
              Andreas Hundt <andi@fischlustig.de>,
              Sven Neumann <neo@directfb.org>,
              Ville Syrjälä <syrjala@sci.fi> and
              Claudio Ciccani <klan@users.sf.net>.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the
   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

//#define DIRECT_ENABLE_DEBUG

#include <config.h>

#include <stdio.h>
#include <string.h>

extern "C" {
#include <direct/messages.h>
}

#include <direct/ToString.h>

#include <egl/dfbegl.h>

D_LOG_DOMAIN( DFBEGL_Config, "DFBEGL/Config", "DirectFB EGL Config" );

namespace DirectFB {

namespace EGL {

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

enum {
   /* types */
   ATTRIB_TYPE_INTEGER,
   ATTRIB_TYPE_BOOLEAN,
   ATTRIB_TYPE_BITMASK,
   ATTRIB_TYPE_ENUM,
   ATTRIB_TYPE_PSEUDO, /* non-queryable */
   ATTRIB_TYPE_PLATFORM, /* platform-dependent */
   /* criteria */
   ATTRIB_CRITERION_EXACT,
   ATTRIB_CRITERION_ATLEAST,
   ATTRIB_CRITERION_MASK,
   ATTRIB_CRITERION_SPECIAL,
   ATTRIB_CRITERION_IGNORE
};

/* EGL spec Table 3.1 and 3.4 */
static const struct {
   EGLint attr;
   EGLint type;
   EGLint criterion;
   EGLint default_value;
} _eglValidationTable[] =
{
   /* core */
   { EGL_BUFFER_SIZE,               ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_RED_SIZE,                  ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_GREEN_SIZE,                ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_BLUE_SIZE,                 ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_LUMINANCE_SIZE,            ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_ALPHA_SIZE,                ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_ALPHA_MASK_SIZE,           ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_BIND_TO_TEXTURE_RGB,       ATTRIB_TYPE_BOOLEAN,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_BIND_TO_TEXTURE_RGBA,      ATTRIB_TYPE_BOOLEAN,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_COLOR_BUFFER_TYPE,         ATTRIB_TYPE_ENUM,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_RGB_BUFFER },
   { EGL_CONFIG_CAVEAT,             ATTRIB_TYPE_ENUM,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_CONFIG_ID,                 ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_CONFORMANT,                ATTRIB_TYPE_BITMASK,
                                    ATTRIB_CRITERION_MASK,
                                    0 },
   { EGL_DEPTH_SIZE,                ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_LEVEL,                     ATTRIB_TYPE_PLATFORM,
                                    ATTRIB_CRITERION_EXACT,
                                    0 },
   { EGL_MAX_PBUFFER_WIDTH,         ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_IGNORE,
                                    0 },
   { EGL_MAX_PBUFFER_HEIGHT,        ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_IGNORE,
                                    0 },
   { EGL_MAX_PBUFFER_PIXELS,        ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_IGNORE,
                                    0 },
   { EGL_MAX_SWAP_INTERVAL,         ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_MIN_SWAP_INTERVAL,         ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_NATIVE_RENDERABLE,         ATTRIB_TYPE_BOOLEAN,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_NATIVE_VISUAL_ID,          ATTRIB_TYPE_PLATFORM,
                                    ATTRIB_CRITERION_IGNORE,
                                    0 },
   { EGL_NATIVE_VISUAL_TYPE,        ATTRIB_TYPE_PLATFORM,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_RENDERABLE_TYPE,           ATTRIB_TYPE_BITMASK,
                                    ATTRIB_CRITERION_MASK,
                                    EGL_OPENGL_ES_BIT },
   { EGL_SAMPLE_BUFFERS,            ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_SAMPLES,                   ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_STENCIL_SIZE,              ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_SURFACE_TYPE,              ATTRIB_TYPE_BITMASK,
                                    ATTRIB_CRITERION_MASK,
                                    EGL_WINDOW_BIT },
   { EGL_TRANSPARENT_TYPE,          ATTRIB_TYPE_ENUM,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_NONE },
   { EGL_TRANSPARENT_RED_VALUE,     ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_TRANSPARENT_GREEN_VALUE,   ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_TRANSPARENT_BLUE_VALUE,    ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_MATCH_NATIVE_PIXMAP,       ATTRIB_TYPE_PSEUDO,
                                    ATTRIB_CRITERION_SPECIAL,
                                    EGL_NONE },
   /* extensions */
   { EGL_Y_INVERTED_NOK,            ATTRIB_TYPE_BOOLEAN,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE }
};

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

GraphicsConfig::GraphicsConfig( Graphics::Implementation &impl )
     :
     Config( &impl ),
     impl( impl )
{
     D_DEBUG_AT( DFBEGL_Config, "EGL::Config::%s( %p )\n", __FUNCTION__, this );

}

GraphicsConfig::~GraphicsConfig()
{
     D_DEBUG_AT( DFBEGL_Config, "EGL::Config::%s( %p )\n", __FUNCTION__, this );

}

DFBResult
GraphicsConfig::CheckOptions( const Graphics::Options &options )
{
     D_DEBUG_AT( DFBEGL_Config, "GraphicsConfig::%s( %p, options %p )\n", __FUNCTION__, this, &options );

     long attr, val, i;
     EGLBoolean matched = EGL_TRUE;

     for (i = 0; i < D_ARRAY_SIZE(_eglValidationTable); i++) {
        long cmp;

        if (_eglValidationTable[i].criterion == ATTRIB_CRITERION_IGNORE)
           continue;

        attr = _eglValidationTable[i].attr;

        cmp = EGL_DONT_CARE;
        cmp = options.GetValue( ToString<EGL::EGLInt>( EGL::EGLInt(attr) ), cmp );
        if (cmp == EGL_DONT_CARE)
           continue;

        GetOption( ToString<EGL::EGLInt>( EGL::EGLInt(attr) ), val );

        D_DEBUG_AT( DFBEGL_Config, "  -> checking '%s', shall be %4ld, is %4ld, criterion %d\n",
                    *ToString<EGL::EGLInt>( EGL::EGLInt(attr) ), cmp, val, _eglValidationTable[i].criterion );

        switch (_eglValidationTable[i].criterion) {
        case ATTRIB_CRITERION_EXACT:
           if (val != cmp) {
              D_DEBUG_AT( DFBEGL_Config, "  -> EXACT FAIL\n" );
              matched = EGL_FALSE;
           }
           else
              D_DEBUG_AT( DFBEGL_Config, "  -> exact match\n" );
           break;
        case ATTRIB_CRITERION_ATLEAST:
           if (val < cmp) {
              D_DEBUG_AT( DFBEGL_Config, "  -> AT LEAST FAIL\n" );
              matched = EGL_FALSE;
           }
           else
              D_DEBUG_AT( DFBEGL_Config, "  -> at least match\n" );
           break;
        case ATTRIB_CRITERION_MASK:
           if ((val & cmp) != cmp) {
              D_DEBUG_AT( DFBEGL_Config, "  -> MASK FAIL\n" );
              matched = EGL_FALSE;
           }
           else
              D_DEBUG_AT( DFBEGL_Config, "  -> mask match\n" );
           break;
        case ATTRIB_CRITERION_SPECIAL:
           /* ignored here */
           D_DEBUG_AT( DFBEGL_Config, "  -> SPECIAL\n" );
           break;
        default:
           D_ASSERT(0);
           break;
        }
     }

     if (!matched)
          return DFB_UNSUPPORTED;

     return Graphics::Config::CheckOptions( options );
}

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

Config::Config( Display          *display,
                Graphics::Config *gfx_config )
     :
     display( display ),
     gfx_config( gfx_config )
{
     D_DEBUG_AT( DFBEGL_Config, "EGL::Config::%s( %p, display %p, gfx_config %p )\n",
                 __FUNCTION__, this, display, gfx_config );

}

Config::~Config()
{
     D_DEBUG_AT( DFBEGL_Config, "EGL::Config::%s( %p )\n", __FUNCTION__, this );
}

EGLint
Config::GetAttrib( EGLint attribute, EGLint *value ) const
{
     D_DEBUG_AT( DFBEGL_Config, "EGL::Config::%s( %p, attribute 0x%08x (%d) '%s' )\n",
                 __FUNCTION__, this, attribute, attribute, *ToString<EGLInt>( EGLInt(attribute) ) );

     if (gfx_config) {
          long v;

//          if (attribute == EGL_SURFACE_TYPE) {
//               *value = EGL_WINDOW_BIT | EGL_PIXMAP_BIT | EGL_PBUFFER_BIT;
//               return EGL_SUCCESS;
//          }

          if (gfx_config->GetOption( ToString<EGLInt>( EGLInt(attribute) ), v ) == DFB_OK) {
               *value = v;
               return EGL_SUCCESS;
          }
     }

     return EGL_BAD_ATTRIBUTE;
}

}

}

<commit_msg>egl: comment out debug message<commit_after>/*
   (c) Copyright 2012-2013  DirectFB integrated media GmbH
   (c) Copyright 2001-2013  The world wide DirectFB Open Source Community (directfb.org)
   (c) Copyright 2000-2004  Convergence (integrated media) GmbH

   All rights reserved.

   Written by Denis Oliver Kropp <dok@directfb.org>,
              Andreas Hundt <andi@fischlustig.de>,
              Sven Neumann <neo@directfb.org>,
              Ville Syrjälä <syrjala@sci.fi> and
              Claudio Ciccani <klan@users.sf.net>.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the
   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.
*/

//#define DIRECT_ENABLE_DEBUG

#include <config.h>

#include <stdio.h>
#include <string.h>

extern "C" {
#include <direct/messages.h>
}

#include <direct/ToString.h>

#include <egl/dfbegl.h>

D_LOG_DOMAIN( DFBEGL_Config, "DFBEGL/Config", "DirectFB EGL Config" );

namespace DirectFB {

namespace EGL {

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

enum {
   /* types */
   ATTRIB_TYPE_INTEGER,
   ATTRIB_TYPE_BOOLEAN,
   ATTRIB_TYPE_BITMASK,
   ATTRIB_TYPE_ENUM,
   ATTRIB_TYPE_PSEUDO, /* non-queryable */
   ATTRIB_TYPE_PLATFORM, /* platform-dependent */
   /* criteria */
   ATTRIB_CRITERION_EXACT,
   ATTRIB_CRITERION_ATLEAST,
   ATTRIB_CRITERION_MASK,
   ATTRIB_CRITERION_SPECIAL,
   ATTRIB_CRITERION_IGNORE
};

/* EGL spec Table 3.1 and 3.4 */
static const struct {
   EGLint attr;
   EGLint type;
   EGLint criterion;
   EGLint default_value;
} _eglValidationTable[] =
{
   /* core */
   { EGL_BUFFER_SIZE,               ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_RED_SIZE,                  ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_GREEN_SIZE,                ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_BLUE_SIZE,                 ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_LUMINANCE_SIZE,            ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_ALPHA_SIZE,                ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_ALPHA_MASK_SIZE,           ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_BIND_TO_TEXTURE_RGB,       ATTRIB_TYPE_BOOLEAN,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_BIND_TO_TEXTURE_RGBA,      ATTRIB_TYPE_BOOLEAN,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_COLOR_BUFFER_TYPE,         ATTRIB_TYPE_ENUM,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_RGB_BUFFER },
   { EGL_CONFIG_CAVEAT,             ATTRIB_TYPE_ENUM,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_CONFIG_ID,                 ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_CONFORMANT,                ATTRIB_TYPE_BITMASK,
                                    ATTRIB_CRITERION_MASK,
                                    0 },
   { EGL_DEPTH_SIZE,                ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_LEVEL,                     ATTRIB_TYPE_PLATFORM,
                                    ATTRIB_CRITERION_EXACT,
                                    0 },
   { EGL_MAX_PBUFFER_WIDTH,         ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_IGNORE,
                                    0 },
   { EGL_MAX_PBUFFER_HEIGHT,        ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_IGNORE,
                                    0 },
   { EGL_MAX_PBUFFER_PIXELS,        ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_IGNORE,
                                    0 },
   { EGL_MAX_SWAP_INTERVAL,         ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_MIN_SWAP_INTERVAL,         ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_NATIVE_RENDERABLE,         ATTRIB_TYPE_BOOLEAN,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_NATIVE_VISUAL_ID,          ATTRIB_TYPE_PLATFORM,
                                    ATTRIB_CRITERION_IGNORE,
                                    0 },
   { EGL_NATIVE_VISUAL_TYPE,        ATTRIB_TYPE_PLATFORM,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_RENDERABLE_TYPE,           ATTRIB_TYPE_BITMASK,
                                    ATTRIB_CRITERION_MASK,
                                    EGL_OPENGL_ES_BIT },
   { EGL_SAMPLE_BUFFERS,            ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_SAMPLES,                   ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_STENCIL_SIZE,              ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_ATLEAST,
                                    0 },
   { EGL_SURFACE_TYPE,              ATTRIB_TYPE_BITMASK,
                                    ATTRIB_CRITERION_MASK,
                                    EGL_WINDOW_BIT },
   { EGL_TRANSPARENT_TYPE,          ATTRIB_TYPE_ENUM,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_NONE },
   { EGL_TRANSPARENT_RED_VALUE,     ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_TRANSPARENT_GREEN_VALUE,   ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_TRANSPARENT_BLUE_VALUE,    ATTRIB_TYPE_INTEGER,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE },
   { EGL_MATCH_NATIVE_PIXMAP,       ATTRIB_TYPE_PSEUDO,
                                    ATTRIB_CRITERION_SPECIAL,
                                    EGL_NONE },
   /* extensions */
   { EGL_Y_INVERTED_NOK,            ATTRIB_TYPE_BOOLEAN,
                                    ATTRIB_CRITERION_EXACT,
                                    EGL_DONT_CARE }
};

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

GraphicsConfig::GraphicsConfig( Graphics::Implementation &impl )
     :
     Config( &impl ),
     impl( impl )
{
     D_DEBUG_AT( DFBEGL_Config, "EGL::Config::%s( %p )\n", __FUNCTION__, this );

}

GraphicsConfig::~GraphicsConfig()
{
     D_DEBUG_AT( DFBEGL_Config, "EGL::Config::%s( %p )\n", __FUNCTION__, this );

}

DFBResult
GraphicsConfig::CheckOptions( const Graphics::Options &options )
{
     D_DEBUG_AT( DFBEGL_Config, "GraphicsConfig::%s( %p, options %p )\n", __FUNCTION__, this, &options );

     long attr, val, i;
     EGLBoolean matched = EGL_TRUE;

     for (i = 0; i < D_ARRAY_SIZE(_eglValidationTable); i++) {
        long cmp;

        if (_eglValidationTable[i].criterion == ATTRIB_CRITERION_IGNORE)
           continue;

        attr = _eglValidationTable[i].attr;

        cmp = EGL_DONT_CARE;
        cmp = options.GetValue( ToString<EGL::EGLInt>( EGL::EGLInt(attr) ), cmp );
        if (cmp == EGL_DONT_CARE)
           continue;

        GetOption( ToString<EGL::EGLInt>( EGL::EGLInt(attr) ), val );

        D_DEBUG_AT( DFBEGL_Config, "  -> checking '%s', shall be %4ld, is %4ld, criterion %d\n",
                    *ToString<EGL::EGLInt>( EGL::EGLInt(attr) ), cmp, val, _eglValidationTable[i].criterion );

        switch (_eglValidationTable[i].criterion) {
        case ATTRIB_CRITERION_EXACT:
           if (val != cmp) {
              D_DEBUG_AT( DFBEGL_Config, "  -> EXACT FAIL\n" );
              matched = EGL_FALSE;
           }
           else
              D_DEBUG_AT( DFBEGL_Config, "  -> exact match\n" );
           break;
        case ATTRIB_CRITERION_ATLEAST:
           if (val < cmp) {
              D_DEBUG_AT( DFBEGL_Config, "  -> AT LEAST FAIL\n" );
              matched = EGL_FALSE;
           }
           else
              D_DEBUG_AT( DFBEGL_Config, "  -> at least match\n" );
           break;
        case ATTRIB_CRITERION_MASK:
           if ((val & cmp) != cmp) {
              D_DEBUG_AT( DFBEGL_Config, "  -> MASK FAIL\n" );
              matched = EGL_FALSE;
           }
           else
              D_DEBUG_AT( DFBEGL_Config, "  -> mask match\n" );
           break;
        case ATTRIB_CRITERION_SPECIAL:
           /* ignored here */
           D_DEBUG_AT( DFBEGL_Config, "  -> SPECIAL\n" );
           break;
        default:
           D_ASSERT(0);
           break;
        }
     }

     if (!matched)
          return DFB_UNSUPPORTED;

     return Graphics::Config::CheckOptions( options );
}

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

Config::Config( Display          *display,
                Graphics::Config *gfx_config )
     :
     display( display ),
     gfx_config( gfx_config )
{
     D_DEBUG_AT( DFBEGL_Config, "EGL::Config::%s( %p, display %p, gfx_config %p )\n",
                 __FUNCTION__, this, display, gfx_config );

}

Config::~Config()
{
     D_DEBUG_AT( DFBEGL_Config, "EGL::Config::%s( %p )\n", __FUNCTION__, this );
}

EGLint
Config::GetAttrib( EGLint attribute, EGLint *value ) const
{
//     D_DEBUG_AT( DFBEGL_Config, "EGL::Config::%s( %p, attribute 0x%08x (%d) '%s' )\n",
//                 __FUNCTION__, this, attribute, attribute, *ToString<EGLInt>( EGLInt(attribute) ) );

     if (gfx_config) {
          long v;

//          if (attribute == EGL_SURFACE_TYPE) {
//               *value = EGL_WINDOW_BIT | EGL_PIXMAP_BIT | EGL_PBUFFER_BIT;
//               return EGL_SUCCESS;
//          }

          if (gfx_config->GetOption( ToString<EGLInt>( EGLInt(attribute) ), v ) == DFB_OK) {
               *value = v;
               return EGL_SUCCESS;
          }
     }

     return EGL_BAD_ATTRIBUTE;
}

}

}

<|endoftext|>


<commit_before>// This file is part of the "x0" project, http://github.com/christianparpart/x0>
//   (c) 2009-2017 Christian Parpart <christian@parpart.family>
//
// Licensed under the MIT License (the "License"); you may not use this
// file except in compliance with the License. You may obtain a copy of
// the License at: http://opensource.org/licenses/MIT

// HTTP semantic tests

#include <xzero/http/mock/Transport.h>
#include <xzero/http/HttpRequestInfo.h>
#include <xzero/http/HttpResponseInfo.h>
#include <xzero/http/HttpRequest.h>
#include <xzero/http/HttpResponse.h>
#include <xzero/http/BadMessage.h>
#include <xzero/http/HttpStatus.h>
#include <xzero/http/HttpFileHandler.h>
#include <xzero/io/MemoryFileRepository.h>
#include <xzero/executor/LocalExecutor.h>
#include <xzero/io/FileUtil.h>
#include <xzero/MimeTypes.h>
#include <xzero/Buffer.h>
#include <xzero/CivilTime.h>
#include <xzero/testing.h>

using namespace xzero;
using namespace xzero::http;

#define HTTP_DATE_FMT "%a, %d %b %Y %T GMT"

class http_HttpFileHandler : public testing::Test { // {{{
 protected:
  MimeTypes mimetypes_;
  UnixTime mtime_;
  MemoryFileRepository vfs_;
  HttpFileHandler staticFileHandler_;

 public:
  http_HttpFileHandler();
  static std::string generateBoundaryID();
  void staticfileHandler(HttpRequest* request, HttpResponse* response);
  std::shared_ptr<File> getFile(const std::string& path);
};

std::string http_HttpFileHandler::generateBoundaryID() {
  return "HelloBoundaryID";
}

http_HttpFileHandler::http_HttpFileHandler()
  : mimetypes_(),
    mtime_(CivilTime(2016, 8, 17, 3, 26, 0, 0, 0)),
    vfs_(mimetypes_),
    staticFileHandler_(&generateBoundaryID) {

  vfs_.insert("/12345.txt", mtime_, "12345");
  vfs_.insert("/fail-perm", mtime_, EPERM);
  vfs_.insert("/fail-access", mtime_, EACCES);
}

std::shared_ptr<File> http_HttpFileHandler::getFile(const std::string& path) {
  return vfs_.getFile(path);
}

void http_HttpFileHandler::staticfileHandler(HttpRequest* request, HttpResponse* response) {
  auto file = vfs_.getFile(request->path());

  HttpStatus status = staticFileHandler_.handle(request, response, file);
  if (!isError(status))
    return;

  response->setStatus(status);
  response->completed();
}
// }}}

/*
 * [x] 200, basic GET
 * [x] 404, file not found
 * [x] 403, permission failure (EPERM, EACCES)
 * [x] (conditional request) If-None-Match
 * [x] (conditional request) If-Match
 * [x] (conditional request) If-Modified-Since
 * [x] (conditional request) If-Unmodified-Since
 * [ ] (conditional request) If-Range
 * [x] (ranged request) full range
 * [ ] (ranged request) invalid range (for example "0-4" instead of "range=0-4")
 * [x] (ranged request) first N bytes
 * [x] (ranged request) last N bytes
 * [x] (ranged request) multiple ranges
 * [x] HEAD on basic file
 * [ ] HEAD on conditional request
 * [ ] HEAD on ranged request
 * [ ] non-GET/HEAD (should result in MethodNotAllowed)
 * [ ] ensure we checked for 412 (Precondition Failed)
 */

TEST_F(http_HttpFileHandler, GET_FileNotFound) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  transport.run(HttpVersion::VERSION_1_1, "GET", "/notfound.txt",
      {{"Host", "test"}}, "");

  EXPECT_EQ(HttpVersion::VERSION_1_1, transport.responseInfo().version());
  EXPECT_EQ(404, static_cast<int>(transport.responseInfo().status()));
}

TEST_F(http_HttpFileHandler, GET_Ok) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  transport.run(HttpVersion::VERSION_1_1, "GET", "/12345.txt",
      {{"Host", "test"}}, "");

  EXPECT_EQ(HttpVersion::VERSION_1_1, transport.responseInfo().version());
  EXPECT_EQ(200, static_cast<int>(transport.responseInfo().status()));
  EXPECT_EQ("12345", transport.responseBody());
}

TEST_F(http_HttpFileHandler, GET_fail_access) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  transport.run(HttpVersion::VERSION_1_1, "GET", "/fail-access",
      {{"Host", "test"}}, "");

  EXPECT_EQ(HttpVersion::VERSION_1_1, transport.responseInfo().version());
  EXPECT_EQ(403, static_cast<int>(transport.responseInfo().status()));
}

TEST_F(http_HttpFileHandler, GET_fail_perm) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  transport.run(HttpVersion::VERSION_1_1, "GET", "/fail-perm",
      {{"Host", "test"}}, "");

  EXPECT_EQ(HttpVersion::VERSION_1_1, transport.responseInfo().version());
  EXPECT_EQ(403, static_cast<int>(transport.responseInfo().status()));
}

TEST_F(http_HttpFileHandler, HEAD_simple) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  transport.run(HttpVersion::VERSION_1_1, "HEAD", "/12345.txt",
      {{"Host", "test"}}, "");

  EXPECT_EQ(HttpVersion::VERSION_1_1, transport.responseInfo().version());
  EXPECT_EQ(200, static_cast<int>(transport.responseInfo().status()));
  EXPECT_EQ("", transport.responseBody());

  EXPECT_EQ("5", transport.responseInfo().getHeader("Content-Length"));
  EXPECT_EQ(0, transport.responseBody().size());
}

TEST_F(http_HttpFileHandler, GET_range_full) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  transport.run(HttpVersion::VERSION_1_1, "GET", "/12345.txt",
      {{"Host", "test"},
       {"Range", "bytes=0-4"}}, "");

  EXPECT_EQ(HttpStatus::PartialContent, transport.responseInfo().status());
  EXPECT_EQ("bytes 0-4/5", transport.responseInfo().getHeader("Content-Range"));
  EXPECT_EQ("12345", transport.responseBody());
}

TEST_F(http_HttpFileHandler, GET_range_first_n) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  transport.run(HttpVersion::VERSION_1_1, "GET", "/12345.txt",
      {{"Host", "test"},
       {"Range", "bytes=0-1"}}, "");

  EXPECT_EQ(HttpStatus::PartialContent, transport.responseInfo().status());
  EXPECT_EQ("bytes 0-1/5", transport.responseInfo().getHeader("Content-Range"));
  EXPECT_EQ("12", transport.responseBody());
}

TEST_F(http_HttpFileHandler, GET_range_last_n) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  transport.run(HttpVersion::VERSION_1_1, "GET", "/12345.txt",
      {{"Host", "test"},
       {"Range", "bytes=-2"}}, "");

  EXPECT_EQ(HttpStatus::PartialContent, transport.responseInfo().status());
  EXPECT_EQ("bytes 3-4/5", transport.responseInfo().getHeader("Content-Range"));
  EXPECT_EQ("45", transport.responseBody());
}

TEST_F(http_HttpFileHandler, GET_range_many) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  transport.run(HttpVersion::VERSION_1_1, "GET", "/12345.txt",
      {{"Host", "test"},
       {"Range", "bytes=0-0,3-3"}}, "");

  EXPECT_EQ(HttpStatus::PartialContent, transport.responseInfo().status());
  EXPECT_EQ("multipart/byteranges; boundary=HelloBoundaryID", transport.responseInfo().getHeader("Content-Type"));
  EXPECT_EQ("\r\n"
            "--HelloBoundaryID\r\n"
            "Content-Type: application/octet-stream\r\n"
            "Content-Range: bytes 0-0/5\r\n"
            "\r\n"
            "1\r\n"
            "--HelloBoundaryID\r\n"
            "Content-Type: application/octet-stream\r\n"
            "Content-Range: bytes 3-3/5\r\n"
            "\r\n"
            "4\r\n"
            "--HelloBoundaryID--\r\n",
            transport.responseBody());
}

TEST_F(http_HttpFileHandler, GET_if_match) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  std::string path = "/12345.txt";
  auto file = getFile(path);

  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-Match", "__" + file->etag()}}, "");
  EXPECT_EQ(HttpStatus::PreconditionFailed, transport.responseInfo().status());

  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-Match", file->etag()}}, "");
  EXPECT_EQ(HttpStatus::Ok, transport.responseInfo().status());
}

TEST_F(http_HttpFileHandler, GET_if_none_match) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  std::string path = "/12345.txt";
  auto file = getFile(path);

  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-None-Match", file->etag()}}, "");
  EXPECT_EQ(HttpStatus::PreconditionFailed, transport.responseInfo().status());

  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-None-Match", "__" + file->etag()}}, "");
  EXPECT_EQ(HttpStatus::Ok, transport.responseInfo().status());
}

TEST_F(http_HttpFileHandler, GET_if_modified_since) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  std::string path = "/12345.txt";
  auto file = getFile(path);

  // test exact-date match
  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-Modified-Since", file->lastModified()}}, "");
  EXPECT_EQ(HttpStatus::NotModified, transport.responseInfo().status());

  // test future-date-match
  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-Modified-Since", (mtime_ + 1_minutes).toString(HTTP_DATE_FMT)}}, "");
  EXPECT_EQ(HttpStatus::NotModified, transport.responseInfo().status());

  // test past-date match
  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-Modified-Since", (mtime_ - 1_minutes).toString(HTTP_DATE_FMT)}}, "");
  EXPECT_EQ(HttpStatus::Ok, transport.responseInfo().status());
}

TEST_F(http_HttpFileHandler, GET_if_unmodified_since) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  std::string path = "/12345.txt";
  auto file = getFile(path);

  // test exact-date match
  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-Unmodified-Since", file->lastModified()}}, "");
  EXPECT_EQ(HttpStatus::Ok, transport.responseInfo().status());
  EXPECT_EQ(5, transport.responseBody().size());

  // test future-date match
  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-Unmodified-Since", (mtime_ + 1_minutes).toString(HTTP_DATE_FMT)}}, "");
  EXPECT_EQ(HttpStatus::Ok, transport.responseInfo().status());
  EXPECT_EQ(5, transport.responseBody().size());

  // test past-date match
  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-Unmodified-Since", (mtime_ - 1_minutes).toString(HTTP_DATE_FMT)}}, "");
  EXPECT_EQ(HttpStatus::PreconditionFailed, transport.responseInfo().status());
}
<commit_msg>[test] HttpFileHandler: implements If-Range tests<commit_after>// This file is part of the "x0" project, http://github.com/christianparpart/x0>
//   (c) 2009-2017 Christian Parpart <christian@parpart.family>
//
// Licensed under the MIT License (the "License"); you may not use this
// file except in compliance with the License. You may obtain a copy of
// the License at: http://opensource.org/licenses/MIT

// HTTP semantic tests

#include <xzero/http/mock/Transport.h>
#include <xzero/http/HttpRequestInfo.h>
#include <xzero/http/HttpResponseInfo.h>
#include <xzero/http/HttpRequest.h>
#include <xzero/http/HttpResponse.h>
#include <xzero/http/BadMessage.h>
#include <xzero/http/HttpStatus.h>
#include <xzero/http/HttpFileHandler.h>
#include <xzero/io/MemoryFileRepository.h>
#include <xzero/executor/LocalExecutor.h>
#include <xzero/io/FileUtil.h>
#include <xzero/MimeTypes.h>
#include <xzero/Buffer.h>
#include <xzero/CivilTime.h>
#include <xzero/testing.h>

using namespace xzero;
using namespace xzero::http;

#define HTTP_DATE_FMT "%a, %d %b %Y %T GMT"

class http_HttpFileHandler : public testing::Test { // {{{
 protected:
  MimeTypes mimetypes_;
  UnixTime mtime_;
  MemoryFileRepository vfs_;
  HttpFileHandler staticFileHandler_;

 public:
  http_HttpFileHandler();
  static std::string generateBoundaryID();
  void staticfileHandler(HttpRequest* request, HttpResponse* response);
  std::shared_ptr<File> getFile(const std::string& path);
};

std::string http_HttpFileHandler::generateBoundaryID() {
  return "HelloBoundaryID";
}

http_HttpFileHandler::http_HttpFileHandler()
  : mimetypes_(),
    mtime_(CivilTime(2016, 8, 17, 3, 26, 0, 0, 0)),
    vfs_(mimetypes_),
    staticFileHandler_(&generateBoundaryID) {

  vfs_.insert("/12345.txt", mtime_, "12345");
  vfs_.insert("/fail-perm", mtime_, EPERM);
  vfs_.insert("/fail-access", mtime_, EACCES);
}

std::shared_ptr<File> http_HttpFileHandler::getFile(const std::string& path) {
  return vfs_.getFile(path);
}

void http_HttpFileHandler::staticfileHandler(HttpRequest* request, HttpResponse* response) {
  auto file = vfs_.getFile(request->path());

  HttpStatus status = staticFileHandler_.handle(request, response, file);
  if (!isError(status))
    return;

  response->setStatus(status);
  response->completed();
}
// }}}

/*
 * [x] 200, basic GET
 * [x] 404, file not found
 * [x] 403, permission failure (EPERM, EACCES)
 * [x] (conditional request) If-None-Match
 * [x] (conditional request) If-Match
 * [x] (conditional request) If-Modified-Since
 * [x] (conditional request) If-Unmodified-Since
 * [x] (conditional request) If-Range
 * [x] (ranged request) full range
 * [ ] (ranged request) invalid range (for example "0-4" instead of "range=0-4")
 * [x] (ranged request) first N bytes
 * [x] (ranged request) last N bytes
 * [x] (ranged request) multiple ranges
 * [x] HEAD on basic file
 * [ ] HEAD on conditional request
 * [ ] HEAD on ranged request
 * [ ] non-GET/HEAD (should result in MethodNotAllowed)
 * [ ] ensure we checked for 412 (Precondition Failed)
 */

TEST_F(http_HttpFileHandler, GET_FileNotFound) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  transport.run(HttpVersion::VERSION_1_1, "GET", "/notfound.txt",
      {{"Host", "test"}}, "");

  EXPECT_EQ(HttpVersion::VERSION_1_1, transport.responseInfo().version());
  EXPECT_EQ(404, static_cast<int>(transport.responseInfo().status()));
}

TEST_F(http_HttpFileHandler, GET_Ok) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  transport.run(HttpVersion::VERSION_1_1, "GET", "/12345.txt",
      {{"Host", "test"}}, "");

  EXPECT_EQ(HttpVersion::VERSION_1_1, transport.responseInfo().version());
  EXPECT_EQ(200, static_cast<int>(transport.responseInfo().status()));
  EXPECT_EQ("12345", transport.responseBody());
}

TEST_F(http_HttpFileHandler, GET_fail_access) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  transport.run(HttpVersion::VERSION_1_1, "GET", "/fail-access",
      {{"Host", "test"}}, "");

  EXPECT_EQ(HttpVersion::VERSION_1_1, transport.responseInfo().version());
  EXPECT_EQ(403, static_cast<int>(transport.responseInfo().status()));
}

TEST_F(http_HttpFileHandler, GET_fail_perm) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  transport.run(HttpVersion::VERSION_1_1, "GET", "/fail-perm",
      {{"Host", "test"}}, "");

  EXPECT_EQ(HttpVersion::VERSION_1_1, transport.responseInfo().version());
  EXPECT_EQ(403, static_cast<int>(transport.responseInfo().status()));
}

TEST_F(http_HttpFileHandler, HEAD_simple) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  transport.run(HttpVersion::VERSION_1_1, "HEAD", "/12345.txt",
      {{"Host", "test"}}, "");

  EXPECT_EQ(HttpVersion::VERSION_1_1, transport.responseInfo().version());
  EXPECT_EQ(200, static_cast<int>(transport.responseInfo().status()));
  EXPECT_EQ("", transport.responseBody());

  EXPECT_EQ("5", transport.responseInfo().getHeader("Content-Length"));
  EXPECT_EQ(0, transport.responseBody().size());
}

TEST_F(http_HttpFileHandler, GET_range_full) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  transport.run(HttpVersion::VERSION_1_1, "GET", "/12345.txt",
      {{"Host", "test"},
       {"Range", "bytes=0-4"}}, "");

  EXPECT_EQ(HttpStatus::PartialContent, transport.responseInfo().status());
  EXPECT_EQ("bytes 0-4/5", transport.responseInfo().getHeader("Content-Range"));
  EXPECT_EQ("12345", transport.responseBody());
}

TEST_F(http_HttpFileHandler, GET_range_first_n) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  transport.run(HttpVersion::VERSION_1_1, "GET", "/12345.txt",
      {{"Host", "test"},
       {"Range", "bytes=0-1"}}, "");

  EXPECT_EQ(HttpStatus::PartialContent, transport.responseInfo().status());
  EXPECT_EQ("bytes 0-1/5", transport.responseInfo().getHeader("Content-Range"));
  EXPECT_EQ("12", transport.responseBody());
}

TEST_F(http_HttpFileHandler, GET_range_last_n) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  transport.run(HttpVersion::VERSION_1_1, "GET", "/12345.txt",
      {{"Host", "test"},
       {"Range", "bytes=-2"}}, "");

  EXPECT_EQ(HttpStatus::PartialContent, transport.responseInfo().status());
  EXPECT_EQ("bytes 3-4/5", transport.responseInfo().getHeader("Content-Range"));
  EXPECT_EQ("45", transport.responseBody());
}

TEST_F(http_HttpFileHandler, GET_range_many) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  transport.run(HttpVersion::VERSION_1_1, "GET", "/12345.txt",
      {{"Host", "test"},
       {"Range", "bytes=0-0,3-3"}}, "");

  EXPECT_EQ(HttpStatus::PartialContent, transport.responseInfo().status());
  EXPECT_EQ("multipart/byteranges; boundary=HelloBoundaryID", transport.responseInfo().getHeader("Content-Type"));
  EXPECT_EQ("\r\n"
            "--HelloBoundaryID\r\n"
            "Content-Type: application/octet-stream\r\n"
            "Content-Range: bytes 0-0/5\r\n"
            "\r\n"
            "1\r\n"
            "--HelloBoundaryID\r\n"
            "Content-Type: application/octet-stream\r\n"
            "Content-Range: bytes 3-3/5\r\n"
            "\r\n"
            "4\r\n"
            "--HelloBoundaryID--\r\n",
            transport.responseBody());
}

TEST_F(http_HttpFileHandler, GET_if_match) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  std::string path = "/12345.txt";
  auto file = getFile(path);

  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-Match", "__" + file->etag()}}, "");
  EXPECT_EQ(HttpStatus::PreconditionFailed, transport.responseInfo().status());

  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-Match", file->etag()}}, "");
  EXPECT_EQ(HttpStatus::Ok, transport.responseInfo().status());
}

TEST_F(http_HttpFileHandler, GET_if_none_match) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  std::string path = "/12345.txt";
  auto file = getFile(path);

  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-None-Match", file->etag()}}, "");
  EXPECT_EQ(HttpStatus::PreconditionFailed, transport.responseInfo().status());

  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-None-Match", "__" + file->etag()}}, "");
  EXPECT_EQ(HttpStatus::Ok, transport.responseInfo().status());
}

TEST_F(http_HttpFileHandler, GET_if_modified_since) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  std::string path = "/12345.txt";
  auto file = getFile(path);

  // test exact-date match
  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-Modified-Since", file->lastModified()}}, "");
  EXPECT_EQ(HttpStatus::NotModified, transport.responseInfo().status());

  // test future-date-match
  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-Modified-Since", (mtime_ + 1_minutes).toString(HTTP_DATE_FMT)}}, "");
  EXPECT_EQ(HttpStatus::NotModified, transport.responseInfo().status());

  // test past-date match
  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-Modified-Since", (mtime_ - 1_minutes).toString(HTTP_DATE_FMT)}}, "");
  EXPECT_EQ(HttpStatus::Ok, transport.responseInfo().status());
}

TEST_F(http_HttpFileHandler, GET_if_unmodified_since) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  std::string path = "/12345.txt";
  auto file = getFile(path);

  // test exact-date match
  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-Unmodified-Since", file->lastModified()}}, "");
  EXPECT_EQ(HttpStatus::Ok, transport.responseInfo().status());
  EXPECT_EQ(5, transport.responseBody().size());

  // test future-date match
  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-Unmodified-Since", (mtime_ + 1_minutes).toString(HTTP_DATE_FMT)}}, "");
  EXPECT_EQ(HttpStatus::Ok, transport.responseInfo().status());
  EXPECT_EQ(5, transport.responseBody().size());

  // test past-date match
  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"If-Unmodified-Since", (mtime_ - 1_minutes).toString(HTTP_DATE_FMT)}}, "");
  EXPECT_EQ(HttpStatus::PreconditionFailed, transport.responseInfo().status());
}

TEST_F(http_HttpFileHandler, GET_if_range_etag) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  std::string path = "/12345.txt";
  auto file = getFile(path);

  // test exact-etag match: receive requested range, as ETag matches
  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"Range", "bytes=0-2"},
       {"If-Range", file->etag()}}, "");
  EXPECT_EQ(HttpStatus::PartialContent, transport.responseInfo().status());
  EXPECT_EQ("123", transport.responseBody());

  // test wrong-etag match: receive full message instead
  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"Range", "bytes=0-2"},
       {"If-Range", "__" + file->etag()}}, "");
  EXPECT_EQ(HttpStatus::Ok, transport.responseInfo().status());
  EXPECT_EQ("12345", transport.responseBody());
}

TEST_F(http_HttpFileHandler, GET_if_range_date) {
  LocalExecutor executor;
  mock::Transport transport(&executor,
      std::bind(&http_HttpFileHandler::staticfileHandler, this,
                std::placeholders::_1, std::placeholders::_2));

  std::string path = "/12345.txt";
  auto file = getFile(path);

  // test exact-etag match: receive requested range, as ETag matches
  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"Range", "bytes=0-2"},
       {"If-Range", file->lastModified()}}, "");
  EXPECT_EQ(HttpStatus::PartialContent, transport.responseInfo().status());
  EXPECT_EQ("123", transport.responseBody());

  // test wrong-etag match: receive full message instead
  transport.run(HttpVersion::VERSION_1_1, "GET", path,
      {{"Host", "test"},
       {"Range", "bytes=0-2"},
       {"If-Range", (mtime_  - 1_minutes).toString(HTTP_DATE_FMT)}}, "");
  EXPECT_EQ(HttpStatus::Ok, transport.responseInfo().status());
  EXPECT_EQ("12345", transport.responseBody());
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 The Native Client Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include <pthread.h>

#include "native_client/src/include/nacl_assert.h"
#include "native_client/tests/performance/perf_test_runner.h"

class TestUncontendedMutexLock : public PerfTest {
 public:
  TestUncontendedMutexLock() {
    ASSERT_EQ(pthread_mutex_init(&mutex_, NULL), 0);
  }

  ~TestUncontendedMutexLock() {
    ASSERT_EQ(pthread_mutex_destroy(&mutex_), 0);
  }

  virtual void run() {
    ASSERT_EQ(pthread_mutex_lock(&mutex_), 0);
    ASSERT_EQ(pthread_mutex_unlock(&mutex_), 0);
  }

 private:
  pthread_mutex_t mutex_;
};
PERF_TEST_DECLARE(TestUncontendedMutexLock)

// Test the overhead of pthread_cond_signal() on a condvar that no
// thread is waiting on.
class TestCondvarSignalNoOp : public PerfTest {
 public:
  TestCondvarSignalNoOp() {
    ASSERT_EQ(pthread_cond_init(&condvar_, NULL), 0);
  }

  ~TestCondvarSignalNoOp() {
    ASSERT_EQ(pthread_cond_destroy(&condvar_), 0);
  }

  virtual void run() {
    ASSERT_EQ(pthread_cond_signal(&condvar_), 0);
  }

 private:
  pthread_cond_t condvar_;
};
PERF_TEST_DECLARE(TestCondvarSignalNoOp)

class TestThreadCreateAndJoin : public PerfTest {
 public:
  virtual void run() {
    pthread_t tid;
    ASSERT_EQ(pthread_create(&tid, NULL, EmptyThread, NULL), 0);
    ASSERT_EQ(pthread_join(tid, NULL), 0);
  }

 private:
  static void *EmptyThread(void *thread_arg) {
    UNREFERENCED_PARAMETER(thread_arg);
    return NULL;
  }
};
PERF_TEST_DECLARE(TestThreadCreateAndJoin)

class TestThreadWakeup : public PerfTest {
 public:
  TestThreadWakeup() {
    ASSERT_EQ(pthread_mutex_init(&mutex_, NULL), 0);
    ASSERT_EQ(pthread_cond_init(&condvar1_, NULL), 0);
    ASSERT_EQ(pthread_cond_init(&condvar2_, NULL), 0);
    state_ = WAIT;
    ASSERT_EQ(pthread_create(&tid_, NULL, Thread, this), 0);
  }

  ~TestThreadWakeup() {
    ASSERT_EQ(pthread_mutex_lock(&mutex_), 0);
    state_ = EXIT;
    ASSERT_EQ(pthread_cond_signal(&condvar1_), 0);
    ASSERT_EQ(pthread_mutex_unlock(&mutex_), 0);

    ASSERT_EQ(pthread_join(tid_, NULL), 0);
    ASSERT_EQ(pthread_cond_destroy(&condvar2_), 0);
    ASSERT_EQ(pthread_cond_destroy(&condvar1_), 0);
    ASSERT_EQ(pthread_mutex_destroy(&mutex_), 0);
  }

  virtual void run() {
    ASSERT_EQ(pthread_mutex_lock(&mutex_), 0);
    state_ = WAKE_CHILD;
    ASSERT_EQ(pthread_cond_signal(&condvar1_), 0);
    while (state_ != REPLY_TO_PARENT)
      ASSERT_EQ(pthread_cond_wait(&condvar2_, &mutex_), 0);
    state_ = WAIT;
    ASSERT_EQ(pthread_mutex_unlock(&mutex_), 0);
  }

 private:
  static void *Thread(void *thread_arg) {
    TestThreadWakeup *obj = (TestThreadWakeup *) thread_arg;
    bool do_exit = false;
    while (!do_exit) {
      ASSERT_EQ(pthread_mutex_lock(&obj->mutex_), 0);
      while (obj->state_ == WAIT)
        ASSERT_EQ(pthread_cond_wait(&obj->condvar1_, &obj->mutex_), 0);
      if (obj->state_ == EXIT) {
        do_exit = true;
      } else {
        obj->state_ = REPLY_TO_PARENT;
        ASSERT_EQ(pthread_cond_signal(&obj->condvar2_), 0);
      }
      ASSERT_EQ(pthread_mutex_unlock(&obj->mutex_), 0);
    }
    return NULL;
  }

  pthread_t tid_;
  pthread_mutex_t mutex_;
  pthread_cond_t condvar1_;
  pthread_cond_t condvar2_;
  enum { WAIT, WAKE_CHILD, REPLY_TO_PARENT, EXIT } state_;
};
PERF_TEST_DECLARE(TestThreadWakeup)
<commit_msg>Performance tests: Fix TestThreadWakeup, which was accidentally spinning<commit_after>/*
 * Copyright (c) 2012 The Native Client Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include <pthread.h>

#include "native_client/src/include/nacl_assert.h"
#include "native_client/tests/performance/perf_test_runner.h"

class TestUncontendedMutexLock : public PerfTest {
 public:
  TestUncontendedMutexLock() {
    ASSERT_EQ(pthread_mutex_init(&mutex_, NULL), 0);
  }

  ~TestUncontendedMutexLock() {
    ASSERT_EQ(pthread_mutex_destroy(&mutex_), 0);
  }

  virtual void run() {
    ASSERT_EQ(pthread_mutex_lock(&mutex_), 0);
    ASSERT_EQ(pthread_mutex_unlock(&mutex_), 0);
  }

 private:
  pthread_mutex_t mutex_;
};
PERF_TEST_DECLARE(TestUncontendedMutexLock)

// Test the overhead of pthread_cond_signal() on a condvar that no
// thread is waiting on.
class TestCondvarSignalNoOp : public PerfTest {
 public:
  TestCondvarSignalNoOp() {
    ASSERT_EQ(pthread_cond_init(&condvar_, NULL), 0);
  }

  ~TestCondvarSignalNoOp() {
    ASSERT_EQ(pthread_cond_destroy(&condvar_), 0);
  }

  virtual void run() {
    ASSERT_EQ(pthread_cond_signal(&condvar_), 0);
  }

 private:
  pthread_cond_t condvar_;
};
PERF_TEST_DECLARE(TestCondvarSignalNoOp)

class TestThreadCreateAndJoin : public PerfTest {
 public:
  virtual void run() {
    pthread_t tid;
    ASSERT_EQ(pthread_create(&tid, NULL, EmptyThread, NULL), 0);
    ASSERT_EQ(pthread_join(tid, NULL), 0);
  }

 private:
  static void *EmptyThread(void *thread_arg) {
    UNREFERENCED_PARAMETER(thread_arg);
    return NULL;
  }
};
PERF_TEST_DECLARE(TestThreadCreateAndJoin)

class TestThreadWakeup : public PerfTest {
 public:
  TestThreadWakeup() {
    ASSERT_EQ(pthread_mutex_init(&mutex_, NULL), 0);
    ASSERT_EQ(pthread_cond_init(&condvar1_, NULL), 0);
    ASSERT_EQ(pthread_cond_init(&condvar2_, NULL), 0);
    state_ = WAIT;
    ASSERT_EQ(pthread_create(&tid_, NULL, Thread, this), 0);
  }

  ~TestThreadWakeup() {
    ASSERT_EQ(pthread_mutex_lock(&mutex_), 0);
    state_ = EXIT;
    ASSERT_EQ(pthread_cond_signal(&condvar1_), 0);
    ASSERT_EQ(pthread_mutex_unlock(&mutex_), 0);

    ASSERT_EQ(pthread_join(tid_, NULL), 0);
    ASSERT_EQ(pthread_cond_destroy(&condvar2_), 0);
    ASSERT_EQ(pthread_cond_destroy(&condvar1_), 0);
    ASSERT_EQ(pthread_mutex_destroy(&mutex_), 0);
  }

  virtual void run() {
    ASSERT_EQ(pthread_mutex_lock(&mutex_), 0);
    state_ = WAKE_CHILD;
    ASSERT_EQ(pthread_cond_signal(&condvar1_), 0);
    while (state_ != REPLY_TO_PARENT)
      ASSERT_EQ(pthread_cond_wait(&condvar2_, &mutex_), 0);
    state_ = WAIT;
    ASSERT_EQ(pthread_mutex_unlock(&mutex_), 0);
  }

 private:
  static void *Thread(void *thread_arg) {
    TestThreadWakeup *obj = (TestThreadWakeup *) thread_arg;
    bool do_exit = false;
    while (!do_exit) {
      ASSERT_EQ(pthread_mutex_lock(&obj->mutex_), 0);
      for (;;) {
        if (obj->state_ == EXIT) {
          do_exit = true;
          break;
        } else if (obj->state_ == WAKE_CHILD) {
          obj->state_ = REPLY_TO_PARENT;
          ASSERT_EQ(pthread_cond_signal(&obj->condvar2_), 0);
          break;
        }
        ASSERT_EQ(pthread_cond_wait(&obj->condvar1_, &obj->mutex_), 0);
      }
      ASSERT_EQ(pthread_mutex_unlock(&obj->mutex_), 0);
    }
    return NULL;
  }

  pthread_t tid_;
  pthread_mutex_t mutex_;
  pthread_cond_t condvar1_;
  pthread_cond_t condvar2_;
  enum { WAIT, WAKE_CHILD, REPLY_TO_PARENT, EXIT } state_;
};
PERF_TEST_DECLARE(TestThreadWakeup)
<|endoftext|>


<commit_before>#include "compare-settings.h"

#include "warnings-disable.h"

WARNINGS_DISABLE
#include <QDebug>
#include <QSettings>
#include <QString>
#include <QStringList>
#include <QVariant>
WARNINGS_ENABLE

//! Compares all the keys in two QSettings files.
bool compareSettings(QSettings *settings, QSettings *target)
{
    // On OSX, QSettings returns a bunch of values for the system
    // itself (e.g., "com/apple/trackpad/enableSecondaryClick",
    // NSNavRecentPlaces").  To avoid this, we only examine the
    // groups that are present in the target config file.
    for(int g = 0; g < target->childGroups().length(); g++)
    {
        QString group = target->childGroups().at(g);

        settings->beginGroup(group);
        target->beginGroup(group);

        // Check length of key list
        QStringList settings_keys = settings->allKeys();
        if(settings_keys.length() != target->allKeys().length())
        {
            qDebug() << "compareSettings: number of keys does not match!"
                     << settings_keys.length() << target->allKeys().length();
            return false;
        }

        // Check each key's value
        for(int i = 0; i < settings_keys.length(); i++)
        {
            QString key = settings_keys.at(i);

            // Skip over keys that will be different
            if((group == "tarsnap") && (key == "machine"))
                continue;
#ifdef Q_OS_OSX
            if((group == "tarsnap") && (key == "cache"))
                continue;
            if((group == "app") && (key == "app_data"))
                continue;
#endif

            // Skip over key(s) that can plausibly be different
            if((group == "tarsnap") && (key == "path"))
                continue;

            // Compare values
            if(settings->value(key) != target->value(key))
            {
                qDebug() << "compareSettings: values do not match!" << key
                         << settings->value(key) << target->value(key);
                return false;
            }
        }
        settings->endGroup();
        target->endGroup();
    }

    return true;
}
<commit_msg>tests/setupwizard: typo<commit_after>#include "compare-settings.h"

#include "warnings-disable.h"

WARNINGS_DISABLE
#include <QDebug>
#include <QSettings>
#include <QString>
#include <QStringList>
#include <QVariant>
WARNINGS_ENABLE

//! Compares all the keys in two QSettings files.
bool compareSettings(QSettings *settings, QSettings *target)
{
    // On OSX, QSettings returns a bunch of values for the system
    // itself (e.g., "com/apple/trackpad/enableSecondaryClick",
    // "NSNavRecentPlaces").  To avoid this, we only examine the
    // groups that are present in the target config file.
    for(int g = 0; g < target->childGroups().length(); g++)
    {
        QString group = target->childGroups().at(g);

        settings->beginGroup(group);
        target->beginGroup(group);

        // Check length of key list
        QStringList settings_keys = settings->allKeys();
        if(settings_keys.length() != target->allKeys().length())
        {
            qDebug() << "compareSettings: number of keys does not match!"
                     << settings_keys.length() << target->allKeys().length();
            return false;
        }

        // Check each key's value
        for(int i = 0; i < settings_keys.length(); i++)
        {
            QString key = settings_keys.at(i);

            // Skip over keys that will be different
            if((group == "tarsnap") && (key == "machine"))
                continue;
#ifdef Q_OS_OSX
            if((group == "tarsnap") && (key == "cache"))
                continue;
            if((group == "app") && (key == "app_data"))
                continue;
#endif

            // Skip over key(s) that can plausibly be different
            if((group == "tarsnap") && (key == "path"))
                continue;

            // Compare values
            if(settings->value(key) != target->value(key))
            {
                qDebug() << "compareSettings: values do not match!" << key
                         << settings->value(key) << target->value(key);
                return false;
            }
        }
        settings->endGroup();
        target->endGroup();
    }

    return true;
}
<|endoftext|>