<commit_before>#include "blackmagicsdk_video_source.h"
#include "deck_link_display_mode_detector.h"
#include <chrono>

namespace gg
{

VideoSourceBlackmagicSDK::VideoSourceBlackmagicSDK()
    : IVideoSource()
    , _frame_rate(0.0)
    , _video_buffer(nullptr)
    , _video_buffer_length(0)
    , _cols(0)
    , _rows(0)
    , _buffer_video_frame(VideoFrame(UYVY))
    , _running(false)
{
    // nop
}

VideoSourceBlackmagicSDK::VideoSourceBlackmagicSDK(size_t deck_link_index,
                                                   ColourSpace colour)
    : IVideoSource(colour)
    , _frame_rate(0.0)
    , _video_buffer(nullptr)
    , _video_buffer_length(0)
    , _cols(0)
    , _rows(0)
    , _buffer_video_frame(VideoFrame(colour, false)) // TODO manage data?
    , _deck_link(nullptr)
    , _deck_link_input(nullptr)
    , _video_input_flags(bmdVideoInputFlagDefault | bmdVideoInputDualStream3D)
    , _12_bit_rgb_to_bgra_converter(nullptr)
    , _bgra_frame_buffers({nullptr, nullptr})
    , _running(false)
{
    // Pixel format, i.e. colour space
    BMDPixelFormat pixel_format;
    switch(_colour)
    {
    case UYVY:
        pixel_format = bmdFormat8BitYUV;
        break;
    case BGRA:
        pixel_format = bmdFormat8BitBGRA;
        break;
    case I420:
    default:
        bail("BlackmagicSDK video source supports only UYVY and BGRA colour spaces");
    }

    // Get an iterator through the available DeckLink ports
    IDeckLinkIterator * deck_link_iterator = CreateDeckLinkIteratorInstance();
    if (deck_link_iterator == nullptr)
        bail("DeckLink drivers do not appear to be installed");

    HRESULT res;

    // Get the desired DeckLink index (port)
    int idx = deck_link_index;
    while ((res = deck_link_iterator->Next(&_deck_link)) == S_OK)
    {
        if (idx == 0)
            break;
        --idx;

        _deck_link->Release();
    }
    if (deck_link_iterator != nullptr)
        deck_link_iterator->Release();
    // No glory: release everything and throw exception
    if (res != S_OK or _deck_link == nullptr)
        bail(
            std::string("Could not get DeckLink device ").append(std::to_string(deck_link_index))
        );

    // Get the input interface of connected DeckLink port
    res = _deck_link->QueryInterface(IID_IDeckLinkInput, reinterpret_cast<void **>(&_deck_link_input));
    // No glory: release everything and throw exception
    if (res != S_OK)
        bail("Could not get the Blackmagic DeckLink input interface");

    // Set the input format (i.e. display mode)
    BMDDisplayMode display_mode;
    std::string error_msg = "";
    if (not detect_input_format(pixel_format, _video_input_flags, display_mode, _frame_rate, error_msg))
    {
        _video_input_flags ^= bmdVideoInputDualStream3D;
        if (not detect_input_format(pixel_format, _video_input_flags, display_mode, _frame_rate, error_msg))
            bail(error_msg);
    }

    // Set this object (IDeckLinkInputCallback instance) as callback
    res = _deck_link_input->SetCallback(this);
    // No glory: release everything and throw exception
    if (res != S_OK)
        bail("Could not set the callback of Blackmagic DeckLink device");

    // Enable video input
    res = _deck_link_input->EnableVideoInput(display_mode,
                                             pixel_format,
                                             _video_input_flags);
    // No glory
    if (res != S_OK)
        bail("Could not enable video input of Blackmagic DeckLink device");

    // Start streaming
    _running = true;
    res = _deck_link_input->StartStreams();
    // No glory: release everything and throw exception
    if (res != S_OK)
    {
        _running = false;
        bail("Could not start streaming from the Blackmagic DeckLink device");
    }

    if (_colour == BGRA)
    {
        _12_bit_rgb_to_bgra_converter = CreateVideoConversionInstance();
        if (_12_bit_rgb_to_bgra_converter == nullptr)
            bail("Could not create colour converter for Blackmagic source");
    }
}

VideoSourceBlackmagicSDK::~VideoSourceBlackmagicSDK()
{
    { // Artificial scope for data lock
        // Make sure streamer thread not trying to access buffer
        std::lock_guard<std::mutex> data_lock_guard(_data_lock);
        _running = false;
    }

    // Stop streaming and disable enabled inputs
    _deck_link_input->SetCallback(nullptr);
    _deck_link_input->StopStreams();
    _deck_link_input->DisableVideoInput();

    // Release DeckLink members
    release_deck_link();
}

bool VideoSourceBlackmagicSDK::get_frame_dimensions(int & width, int & height)
{
    // Make sure only this thread is accessing the cols and rows members now
    std::lock_guard<std::mutex> data_lock_guard(_data_lock);
    if (_cols <= 0 or _rows <= 0)
        return false;

    width = _cols;
    height = _rows;
    return true;
}

bool VideoSourceBlackmagicSDK::get_frame(VideoFrame & frame)
{
    if (frame.colour() != _colour)
        return false;

    // Make sure only this thread is accessing the video frame data now
    std::lock_guard<std::mutex> data_lock_guard(_data_lock);

    if (_video_buffer_length > 0 and _cols > 0 and _rows > 0)
    {
        frame.init_from_specs(_video_buffer, _video_buffer_length, _cols, _rows);
        return true;
    }
    else
        return false;
}

double VideoSourceBlackmagicSDK::get_frame_rate()
{
    return _frame_rate;
}

void VideoSourceBlackmagicSDK::set_sub_frame(int x, int y, int width, int height)
{
    // issue #147
    throw VideoSourceError("Blackmagic does not support cropping yet");
}

void VideoSourceBlackmagicSDK::get_full_frame()
{
    // nop: set_sub_frame currently not implemented
}

HRESULT STDMETHODCALLTYPE VideoSourceBlackmagicSDK::QueryInterface(REFIID iid, LPVOID * ppv)
{
    return E_NOINTERFACE;
}

ULONG STDMETHODCALLTYPE VideoSourceBlackmagicSDK::AddRef(void)
{
    __sync_add_and_fetch(&_n_ref, 1);
    return _n_ref;
}

ULONG STDMETHODCALLTYPE VideoSourceBlackmagicSDK::Release(void)
{
    __sync_sub_and_fetch(&_n_ref, 1);
    return _n_ref;
}

HRESULT STDMETHODCALLTYPE VideoSourceBlackmagicSDK::VideoInputFormatChanged(
    BMDVideoInputFormatChangedEvents events,
    IDeckLinkDisplayMode * display_mode,
    BMDDetectedVideoInputFormatFlags format_flags
)
{
    // not supported yet: see issue #149
    return S_OK;
}

HRESULT STDMETHODCALLTYPE VideoSourceBlackmagicSDK::VideoInputFrameArrived(
    IDeckLinkVideoInputFrame * video_frame,
    IDeckLinkAudioInputPacket * audio_packet
)
{
    if (not _running)
        // nop if not running!
        return S_OK;

    // Not processing the audio packet, but only the video
    // frame for the time being
    if (video_frame == nullptr)
        // nop if no data
        return S_OK;

    // Nr. of bytes of received data
    size_t n_bytes = video_frame->GetRowBytes() * video_frame->GetHeight();
    if (is_stereo())
        n_bytes *= 2;

    { // Artificial scope for data lock
        // Make sure only this thread is accessing the buffer now
        std::lock_guard<std::mutex> data_lock_guard(_data_lock);

        // Extend buffer if more memory needed than already allocated
        if (n_bytes > _video_buffer_length)
            _video_buffer = reinterpret_cast<uint8_t *>(
                        realloc(_video_buffer, n_bytes * sizeof(uint8_t))
            );
        if (_video_buffer == nullptr) // something's terribly wrong!
            // nop if something's terribly wrong!
            return S_OK;

        // Get the new data into the buffer
        HRESULT res = video_frame->GetBytes(
            reinterpret_cast<void **>(&_video_buffer)
        );
        // If data could not be read into the buffer, return
        if (FAILED(res))
            return res;

        if (is_stereo())
        {
            IDeckLinkVideoFrame *right_eye_frame = nullptr;
            IDeckLinkVideoFrame3DExtensions *three_d_extensions = nullptr;
            if ((video_frame->QueryInterface(
                    IID_IDeckLinkVideoFrame3DExtensions,
                    (void **) &three_d_extensions) != S_OK) ||
                (three_d_extensions->GetFrameForRightEye(
                    &right_eye_frame) != S_OK))
            {
                right_eye_frame = nullptr;
            }

            if (three_d_extensions != nullptr)
                three_d_extensions->Release();

            if (right_eye_frame != nullptr)
            {
                res = right_eye_frame->GetBytes(
                    reinterpret_cast<void **>(&_video_buffer[n_bytes / 2])
                );
                right_eye_frame->Release();
                // If data could not be read into the buffer, return
                if (FAILED(res))
                    return res;
            }
        }

        // Set video frame specs according to new data
        _video_buffer_length = n_bytes;
        _cols = video_frame->GetWidth();
        _rows = video_frame->GetHeight();

        // Propagate new video frame to observers
        _buffer_video_frame.init_from_specs(
            _video_buffer, _video_buffer_length, _cols, _rows,
            is_stereo() ? 2 : 1
        );
    }

    this->notify(_buffer_video_frame);

    // Everything went fine, return success
    return S_OK;
}

void VideoSourceBlackmagicSDK::release_deck_link() noexcept
{
    if (_deck_link_input != nullptr)
    {
        _deck_link_input->Release();
        _deck_link_input = nullptr;
    }

    if (_deck_link != nullptr)
        _deck_link->Release();
}

bool VideoSourceBlackmagicSDK::detect_input_format(BMDPixelFormat pixel_format,
                                                   BMDVideoInputFlags & video_input_flags,
                                                   BMDDisplayMode & display_mode,
                                                   double & frame_rate,
                                                   std::string & error_msg) noexcept
{
    std::vector<BMDDisplayMode> display_modes =
    {
        bmdModeHD1080p6000, bmdModeHD1080p5994,
        bmdModeHD1080p50,
        bmdModeHD1080p30, bmdModeHD1080p2997,
        bmdModeHD1080p25, bmdModeHD1080p24, bmdModeHD1080p2398,
        bmdModeHD1080i6000, bmdModeHD1080i5994,
        bmdModeHD1080i50,
        bmdModeHD720p60, bmdModeHD720p5994,
        bmdModeHD720p50,
        bmdMode4K2160p60, bmdMode4K2160p5994,
        bmdMode4K2160p50,
        bmdMode4K2160p30, bmdMode4K2160p2997,
        bmdMode4K2160p25, bmdMode4K2160p24, bmdMode4K2160p2398,
        bmdMode2k25, bmdMode2k24, bmdMode2k2398,
    };

    DeckLinkDisplayModeDetector detector(
                _deck_link_input,
                display_modes, pixel_format, video_input_flags
    );
    BMDDisplayMode display_mode_ = detector.get_display_mode();
    if (display_mode_ != bmdModeUnknown)
    {
        frame_rate = detector.get_frame_rate();
        display_mode = display_mode_;
        video_input_flags = detector.get_video_input_flags();
        return true;
    }
    else
    {
        error_msg = detector.get_error_msg();
        return false;
    }
}

}
<commit_msg>Issue #30: releasing DeckLink converter upon destruction<commit_after>#include "blackmagicsdk_video_source.h"
#include "deck_link_display_mode_detector.h"
#include <chrono>

namespace gg
{

VideoSourceBlackmagicSDK::VideoSourceBlackmagicSDK()
    : IVideoSource()
    , _frame_rate(0.0)
    , _video_buffer(nullptr)
    , _video_buffer_length(0)
    , _cols(0)
    , _rows(0)
    , _buffer_video_frame(VideoFrame(UYVY))
    , _running(false)
{
    // nop
}

VideoSourceBlackmagicSDK::VideoSourceBlackmagicSDK(size_t deck_link_index,
                                                   ColourSpace colour)
    : IVideoSource(colour)
    , _frame_rate(0.0)
    , _video_buffer(nullptr)
    , _video_buffer_length(0)
    , _cols(0)
    , _rows(0)
    , _buffer_video_frame(VideoFrame(colour, false)) // TODO manage data?
    , _deck_link(nullptr)
    , _deck_link_input(nullptr)
    , _video_input_flags(bmdVideoInputFlagDefault | bmdVideoInputDualStream3D)
    , _12_bit_rgb_to_bgra_converter(nullptr)
    , _bgra_frame_buffers({nullptr, nullptr})
    , _running(false)
{
    // Pixel format, i.e. colour space
    BMDPixelFormat pixel_format;
    switch(_colour)
    {
    case UYVY:
        pixel_format = bmdFormat8BitYUV;
        break;
    case BGRA:
        pixel_format = bmdFormat8BitBGRA;
        break;
    case I420:
    default:
        bail("BlackmagicSDK video source supports only UYVY and BGRA colour spaces");
    }

    // Get an iterator through the available DeckLink ports
    IDeckLinkIterator * deck_link_iterator = CreateDeckLinkIteratorInstance();
    if (deck_link_iterator == nullptr)
        bail("DeckLink drivers do not appear to be installed");

    HRESULT res;

    // Get the desired DeckLink index (port)
    int idx = deck_link_index;
    while ((res = deck_link_iterator->Next(&_deck_link)) == S_OK)
    {
        if (idx == 0)
            break;
        --idx;

        _deck_link->Release();
    }
    if (deck_link_iterator != nullptr)
        deck_link_iterator->Release();
    // No glory: release everything and throw exception
    if (res != S_OK or _deck_link == nullptr)
        bail(
            std::string("Could not get DeckLink device ").append(std::to_string(deck_link_index))
        );

    // Get the input interface of connected DeckLink port
    res = _deck_link->QueryInterface(IID_IDeckLinkInput, reinterpret_cast<void **>(&_deck_link_input));
    // No glory: release everything and throw exception
    if (res != S_OK)
        bail("Could not get the Blackmagic DeckLink input interface");

    // Set the input format (i.e. display mode)
    BMDDisplayMode display_mode;
    std::string error_msg = "";
    if (not detect_input_format(pixel_format, _video_input_flags, display_mode, _frame_rate, error_msg))
    {
        _video_input_flags ^= bmdVideoInputDualStream3D;
        if (not detect_input_format(pixel_format, _video_input_flags, display_mode, _frame_rate, error_msg))
            bail(error_msg);
    }

    // Set this object (IDeckLinkInputCallback instance) as callback
    res = _deck_link_input->SetCallback(this);
    // No glory: release everything and throw exception
    if (res != S_OK)
        bail("Could not set the callback of Blackmagic DeckLink device");

    // Enable video input
    res = _deck_link_input->EnableVideoInput(display_mode,
                                             pixel_format,
                                             _video_input_flags);
    // No glory
    if (res != S_OK)
        bail("Could not enable video input of Blackmagic DeckLink device");

    // Start streaming
    _running = true;
    res = _deck_link_input->StartStreams();
    // No glory: release everything and throw exception
    if (res != S_OK)
    {
        _running = false;
        bail("Could not start streaming from the Blackmagic DeckLink device");
    }

    if (_colour == BGRA)
    {
        _12_bit_rgb_to_bgra_converter = CreateVideoConversionInstance();
        if (_12_bit_rgb_to_bgra_converter == nullptr)
            bail("Could not create colour converter for Blackmagic source");
    }
}

VideoSourceBlackmagicSDK::~VideoSourceBlackmagicSDK()
{
    { // Artificial scope for data lock
        // Make sure streamer thread not trying to access buffer
        std::lock_guard<std::mutex> data_lock_guard(_data_lock);
        _running = false;
    }

    // Stop streaming and disable enabled inputs
    _deck_link_input->SetCallback(nullptr);
    _deck_link_input->StopStreams();
    _deck_link_input->DisableVideoInput();

    // Release DeckLink members
    release_deck_link();
}

bool VideoSourceBlackmagicSDK::get_frame_dimensions(int & width, int & height)
{
    // Make sure only this thread is accessing the cols and rows members now
    std::lock_guard<std::mutex> data_lock_guard(_data_lock);
    if (_cols <= 0 or _rows <= 0)
        return false;

    width = _cols;
    height = _rows;
    return true;
}

bool VideoSourceBlackmagicSDK::get_frame(VideoFrame & frame)
{
    if (frame.colour() != _colour)
        return false;

    // Make sure only this thread is accessing the video frame data now
    std::lock_guard<std::mutex> data_lock_guard(_data_lock);

    if (_video_buffer_length > 0 and _cols > 0 and _rows > 0)
    {
        frame.init_from_specs(_video_buffer, _video_buffer_length, _cols, _rows);
        return true;
    }
    else
        return false;
}

double VideoSourceBlackmagicSDK::get_frame_rate()
{
    return _frame_rate;
}

void VideoSourceBlackmagicSDK::set_sub_frame(int x, int y, int width, int height)
{
    // issue #147
    throw VideoSourceError("Blackmagic does not support cropping yet");
}

void VideoSourceBlackmagicSDK::get_full_frame()
{
    // nop: set_sub_frame currently not implemented
}

HRESULT STDMETHODCALLTYPE VideoSourceBlackmagicSDK::QueryInterface(REFIID iid, LPVOID * ppv)
{
    return E_NOINTERFACE;
}

ULONG STDMETHODCALLTYPE VideoSourceBlackmagicSDK::AddRef(void)
{
    __sync_add_and_fetch(&_n_ref, 1);
    return _n_ref;
}

ULONG STDMETHODCALLTYPE VideoSourceBlackmagicSDK::Release(void)
{
    __sync_sub_and_fetch(&_n_ref, 1);
    return _n_ref;
}

HRESULT STDMETHODCALLTYPE VideoSourceBlackmagicSDK::VideoInputFormatChanged(
    BMDVideoInputFormatChangedEvents events,
    IDeckLinkDisplayMode * display_mode,
    BMDDetectedVideoInputFormatFlags format_flags
)
{
    // not supported yet: see issue #149
    return S_OK;
}

HRESULT STDMETHODCALLTYPE VideoSourceBlackmagicSDK::VideoInputFrameArrived(
    IDeckLinkVideoInputFrame * video_frame,
    IDeckLinkAudioInputPacket * audio_packet
)
{
    if (not _running)
        // nop if not running!
        return S_OK;

    // Not processing the audio packet, but only the video
    // frame for the time being
    if (video_frame == nullptr)
        // nop if no data
        return S_OK;

    // Nr. of bytes of received data
    size_t n_bytes = video_frame->GetRowBytes() * video_frame->GetHeight();
    if (is_stereo())
        n_bytes *= 2;

    { // Artificial scope for data lock
        // Make sure only this thread is accessing the buffer now
        std::lock_guard<std::mutex> data_lock_guard(_data_lock);

        // Extend buffer if more memory needed than already allocated
        if (n_bytes > _video_buffer_length)
            _video_buffer = reinterpret_cast<uint8_t *>(
                        realloc(_video_buffer, n_bytes * sizeof(uint8_t))
            );
        if (_video_buffer == nullptr) // something's terribly wrong!
            // nop if something's terribly wrong!
            return S_OK;

        // Get the new data into the buffer
        HRESULT res = video_frame->GetBytes(
            reinterpret_cast<void **>(&_video_buffer)
        );
        // If data could not be read into the buffer, return
        if (FAILED(res))
            return res;

        if (is_stereo())
        {
            IDeckLinkVideoFrame *right_eye_frame = nullptr;
            IDeckLinkVideoFrame3DExtensions *three_d_extensions = nullptr;
            if ((video_frame->QueryInterface(
                    IID_IDeckLinkVideoFrame3DExtensions,
                    (void **) &three_d_extensions) != S_OK) ||
                (three_d_extensions->GetFrameForRightEye(
                    &right_eye_frame) != S_OK))
            {
                right_eye_frame = nullptr;
            }

            if (three_d_extensions != nullptr)
                three_d_extensions->Release();

            if (right_eye_frame != nullptr)
            {
                res = right_eye_frame->GetBytes(
                    reinterpret_cast<void **>(&_video_buffer[n_bytes / 2])
                );
                right_eye_frame->Release();
                // If data could not be read into the buffer, return
                if (FAILED(res))
                    return res;
            }
        }

        // Set video frame specs according to new data
        _video_buffer_length = n_bytes;
        _cols = video_frame->GetWidth();
        _rows = video_frame->GetHeight();

        // Propagate new video frame to observers
        _buffer_video_frame.init_from_specs(
            _video_buffer, _video_buffer_length, _cols, _rows,
            is_stereo() ? 2 : 1
        );
    }

    this->notify(_buffer_video_frame);

    // Everything went fine, return success
    return S_OK;
}

void VideoSourceBlackmagicSDK::release_deck_link() noexcept
{
    if (_deck_link_input != nullptr)
    {
        _deck_link_input->Release();
        _deck_link_input = nullptr;
    }

    if (_deck_link != nullptr)
        _deck_link->Release();

    if (_12_bit_rgb_to_bgra_converter != nullptr)
    {
        _12_bit_rgb_to_bgra_converter->Release();
        _12_bit_rgb_to_bgra_converter = nullptr;
    }
}

bool VideoSourceBlackmagicSDK::detect_input_format(BMDPixelFormat pixel_format,
                                                   BMDVideoInputFlags & video_input_flags,
                                                   BMDDisplayMode & display_mode,
                                                   double & frame_rate,
                                                   std::string & error_msg) noexcept
{
    std::vector<BMDDisplayMode> display_modes =
    {
        bmdModeHD1080p6000, bmdModeHD1080p5994,
        bmdModeHD1080p50,
        bmdModeHD1080p30, bmdModeHD1080p2997,
        bmdModeHD1080p25, bmdModeHD1080p24, bmdModeHD1080p2398,
        bmdModeHD1080i6000, bmdModeHD1080i5994,
        bmdModeHD1080i50,
        bmdModeHD720p60, bmdModeHD720p5994,
        bmdModeHD720p50,
        bmdMode4K2160p60, bmdMode4K2160p5994,
        bmdMode4K2160p50,
        bmdMode4K2160p30, bmdMode4K2160p2997,
        bmdMode4K2160p25, bmdMode4K2160p24, bmdMode4K2160p2398,
        bmdMode2k25, bmdMode2k24, bmdMode2k2398,
    };

    DeckLinkDisplayModeDetector detector(
                _deck_link_input,
                display_modes, pixel_format, video_input_flags
    );
    BMDDisplayMode display_mode_ = detector.get_display_mode();
    if (display_mode_ != bmdModeUnknown)
    {
        frame_rate = detector.get_frame_rate();
        display_mode = display_mode_;
        video_input_flags = detector.get_video_input_flags();
        return true;
    }
    else
    {
        error_msg = detector.get_error_msg();
        return false;
    }
}

}
<|endoftext|>


<commit_before>//===-- TestCompletion.cpp --------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lldb/Interpreter/CommandCompletions.h"
#include "lldb/Utility/StringList.h"
#include "lldb/Utility/TildeExpressionResolver.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

#include "TestingSupport/MockTildeExpressionResolver.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"

namespace fs = llvm::sys::fs;
namespace path = llvm::sys::path;
using namespace llvm;
using namespace lldb_private;

#define ASSERT_NO_ERROR(x)                                                     \
  if (std::error_code ASSERT_NO_ERROR_ec = x) {                                \
    SmallString<128> MessageStorage;                                           \
    raw_svector_ostream Message(MessageStorage);                               \
    Message << #x ": did not return errc::success.\n"                          \
            << "error number: " << ASSERT_NO_ERROR_ec.value() << "\n"          \
            << "error message: " << ASSERT_NO_ERROR_ec.message() << "\n";      \
    GTEST_FATAL_FAILURE_(MessageStorage.c_str());                              \
  } else {                                                                     \
  }

namespace {

class CompletionTest : public testing::Test {
protected:
  /// Unique temporary directory in which all created filesystem entities must
  /// be placed. It is removed at the end of the test suite.
  SmallString<128> BaseDir;

  /// The working directory that we got when starting the test. Every test
  /// should chdir into this directory first because some tests maybe chdir
  /// into another one during their run.
  static SmallString<128> OriginalWorkingDir;

  SmallString<128> DirFoo;
  SmallString<128> DirFooA;
  SmallString<128> DirFooB;
  SmallString<128> DirFooC;
  SmallString<128> DirBar;
  SmallString<128> DirBaz;
  SmallString<128> DirTestFolder;
  SmallString<128> DirNested;

  SmallString<128> FileAA;
  SmallString<128> FileAB;
  SmallString<128> FileAC;
  SmallString<128> FileFoo;
  SmallString<128> FileBar;
  SmallString<128> FileBaz;

  void SetUp() override {
    // chdir back into the original working dir this test binary started with.
    // A previous test may have have changed the working dir.
    ASSERT_NO_ERROR(fs::set_current_path(OriginalWorkingDir));

    // Get the name of the current test. To prevent that by chance two tests
    // get the same temporary directory if createUniqueDirectory fails.
    auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
    ASSERT_TRUE(test_info != nullptr);
    std::string name = test_info->name();
    ASSERT_NO_ERROR(fs::createUniqueDirectory("FsCompletion-" + name, BaseDir));

    const char *DirNames[] = {"foo", "fooa", "foob",        "fooc",
                              "bar", "baz",  "test_folder", "foo/nested"};
    const char *FileNames[] = {"aa1234.tmp",  "ab1234.tmp",  "ac1234.tmp",
                               "foo1234.tmp", "bar1234.tmp", "baz1234.tmp"};
    SmallString<128> *Dirs[] = {&DirFoo, &DirFooA, &DirFooB,       &DirFooC,
                                &DirBar, &DirBaz,  &DirTestFolder, &DirNested};
    for (auto Dir : llvm::zip(DirNames, Dirs)) {
      auto &Path = *std::get<1>(Dir);
      Path = BaseDir;
      path::append(Path, std::get<0>(Dir));
      ASSERT_NO_ERROR(fs::create_directories(Path));
    }

    SmallString<128> *Files[] = {&FileAA,  &FileAB,  &FileAC,
                                 &FileFoo, &FileBar, &FileBaz};
    for (auto File : llvm::zip(FileNames, Files)) {
      auto &Path = *std::get<1>(File);
      Path = BaseDir;
      path::append(Path, std::get<0>(File));
      int FD;
      ASSERT_NO_ERROR(fs::createUniqueFile(Path, FD, Path));
      ::close(FD);
    }
  }

  static void SetUpTestCase() {
    ASSERT_NO_ERROR(fs::current_path(OriginalWorkingDir));
  }

  void TearDown() override { ASSERT_NO_ERROR(fs::remove_directories(BaseDir)); }

  static bool HasEquivalentFile(const Twine &Path, const StringList &Paths) {
    for (size_t I = 0; I < Paths.GetSize(); ++I) {
      if (fs::equivalent(Path, Paths[I]))
        return true;
    }
    return false;
  }

  void DoDirCompletions(const Twine &Prefix,
                        StandardTildeExpressionResolver &Resolver,
                        StringList &Results) {
    // When a partial name matches, it returns all matches.  If it matches both
    // a full name AND some partial names, it returns all of them.
    uint32_t Count =
        CommandCompletions::DiskDirectories(Prefix + "foo", Results, Resolver);
    ASSERT_EQ(4u, Count);
    ASSERT_EQ(Count, Results.GetSize());
    EXPECT_TRUE(HasEquivalentFile(DirFoo, Results));
    EXPECT_TRUE(HasEquivalentFile(DirFooA, Results));
    EXPECT_TRUE(HasEquivalentFile(DirFooB, Results));
    EXPECT_TRUE(HasEquivalentFile(DirFooC, Results));

    // If it matches only partial names, it still works as expected.
    Count = CommandCompletions::DiskDirectories(Twine(Prefix) + "b", Results,
                                                Resolver);
    ASSERT_EQ(2u, Count);
    ASSERT_EQ(Count, Results.GetSize());
    EXPECT_TRUE(HasEquivalentFile(DirBar, Results));
    EXPECT_TRUE(HasEquivalentFile(DirBaz, Results));
  }
};

SmallString<128> CompletionTest::OriginalWorkingDir;
}

static std::vector<std::string> toVector(const StringList &SL) {
  std::vector<std::string> Result;
  for (size_t Idx = 0; Idx < SL.GetSize(); ++Idx)
    Result.push_back(SL[Idx]);
  return Result;
}
using testing::UnorderedElementsAre;

TEST_F(CompletionTest, DirCompletionAbsolute) {
  // All calls to DiskDirectories() return only directories, even when
  // there are files which also match.  The tests below all check this
  // by asserting an exact result count, and verifying against known
  // folders.

  std::string Prefixes[] = {(Twine(BaseDir) + "/").str(), ""};

  StandardTildeExpressionResolver Resolver;
  StringList Results;

  // When a directory is specified that doesn't end in a slash, it searches
  // for that directory, not items under it.
  size_t Count =
      CommandCompletions::DiskDirectories(BaseDir, Results, Resolver);
  ASSERT_EQ(1u, Count);
  ASSERT_EQ(Count, Results.GetSize());
  EXPECT_TRUE(HasEquivalentFile(BaseDir, Results));

  // When the same directory ends with a slash, it finds all children.
  Count = CommandCompletions::DiskDirectories(Prefixes[0], Results, Resolver);
  ASSERT_EQ(7u, Count);
  ASSERT_EQ(Count, Results.GetSize());
  EXPECT_TRUE(HasEquivalentFile(DirFoo, Results));
  EXPECT_TRUE(HasEquivalentFile(DirFooA, Results));
  EXPECT_TRUE(HasEquivalentFile(DirFooB, Results));
  EXPECT_TRUE(HasEquivalentFile(DirFooC, Results));
  EXPECT_TRUE(HasEquivalentFile(DirBar, Results));
  EXPECT_TRUE(HasEquivalentFile(DirBaz, Results));
  EXPECT_TRUE(HasEquivalentFile(DirTestFolder, Results));

  DoDirCompletions(Twine(BaseDir) + "/", Resolver, Results);
  llvm::sys::fs::set_current_path(BaseDir);
  DoDirCompletions("", Resolver, Results);
}

TEST_F(CompletionTest, FileCompletionAbsolute) {
  // All calls to DiskFiles() return both files and directories  The tests below
  // all check this by asserting an exact result count, and verifying against
  // known folders.

  StandardTildeExpressionResolver Resolver;
  StringList Results;
  // When an item is specified that doesn't end in a slash but exactly matches
  // one item, it returns that item.
  size_t Count = CommandCompletions::DiskFiles(Twine(BaseDir) + "/fooa",
                                               Results, Resolver);
  ASSERT_EQ(1u, Count);
  ASSERT_EQ(Count, Results.GetSize());
  EXPECT_TRUE(HasEquivalentFile(DirFooA, Results));

  // The previous check verified a directory match.  But it should work for
  // files too.
  Count =
      CommandCompletions::DiskFiles(Twine(BaseDir) + "/aa", Results, Resolver);
  ASSERT_EQ(1u, Count);
  ASSERT_EQ(Count, Results.GetSize());
  EXPECT_TRUE(HasEquivalentFile(FileAA, Results));

  // When it ends with a slash, it should find all files and directories.
  Count =
      CommandCompletions::DiskFiles(Twine(BaseDir) + "/", Results, Resolver);
  ASSERT_EQ(13u, Count);
  ASSERT_EQ(Count, Results.GetSize());
  EXPECT_TRUE(HasEquivalentFile(DirFoo, Results));
  EXPECT_TRUE(HasEquivalentFile(DirFooA, Results));
  EXPECT_TRUE(HasEquivalentFile(DirFooB, Results));
  EXPECT_TRUE(HasEquivalentFile(DirFooC, Results));
  EXPECT_TRUE(HasEquivalentFile(DirBar, Results));
  EXPECT_TRUE(HasEquivalentFile(DirBaz, Results));
  EXPECT_TRUE(HasEquivalentFile(DirTestFolder, Results));

  EXPECT_TRUE(HasEquivalentFile(FileAA, Results));
  EXPECT_TRUE(HasEquivalentFile(FileAB, Results));
  EXPECT_TRUE(HasEquivalentFile(FileAC, Results));
  EXPECT_TRUE(HasEquivalentFile(FileFoo, Results));
  EXPECT_TRUE(HasEquivalentFile(FileBar, Results));
  EXPECT_TRUE(HasEquivalentFile(FileBaz, Results));

  // When a partial name matches, it returns all file & directory matches.
  Count =
      CommandCompletions::DiskFiles(Twine(BaseDir) + "/foo", Results, Resolver);
  ASSERT_EQ(5u, Count);
  ASSERT_EQ(Count, Results.GetSize());
  EXPECT_TRUE(HasEquivalentFile(DirFoo, Results));
  EXPECT_TRUE(HasEquivalentFile(DirFooA, Results));
  EXPECT_TRUE(HasEquivalentFile(DirFooB, Results));
  EXPECT_TRUE(HasEquivalentFile(DirFooC, Results));
  EXPECT_TRUE(HasEquivalentFile(FileFoo, Results));
}

TEST_F(CompletionTest, DirCompletionUsername) {
  MockTildeExpressionResolver Resolver("James", BaseDir);
  Resolver.AddKnownUser("Kirk", DirFooB);
  Resolver.AddKnownUser("Lars", DirFooC);
  Resolver.AddKnownUser("Jason", DirFoo);
  Resolver.AddKnownUser("Larry", DirFooA);
  std::string sep = path::get_separator();

  // Just resolving current user's home directory by itself should return the
  // directory.
  StringList Results;
  size_t Count = CommandCompletions::DiskDirectories("~", Results, Resolver);
  EXPECT_EQ(Count, Results.GetSize());
  EXPECT_THAT(toVector(Results), UnorderedElementsAre("~" + sep));

  // With a slash appended, it should return all items in the directory.
  Count = CommandCompletions::DiskDirectories("~/", Results, Resolver);
  EXPECT_THAT(toVector(Results),
              UnorderedElementsAre(
                  "~/foo" + sep, "~/fooa" + sep, "~/foob" + sep, "~/fooc" + sep,
                  "~/bar" + sep, "~/baz" + sep, "~/test_folder" + sep));
  EXPECT_EQ(Count, Results.GetSize());

  // Check that we can complete directories in nested paths
  Count = CommandCompletions::DiskDirectories("~/foo/", Results, Resolver);
  EXPECT_EQ(Count, Results.GetSize());
  EXPECT_THAT(toVector(Results), UnorderedElementsAre("~/foo/nested" + sep));

  Count = CommandCompletions::DiskDirectories("~/foo/nes", Results, Resolver);
  EXPECT_EQ(Count, Results.GetSize());
  EXPECT_THAT(toVector(Results), UnorderedElementsAre("~/foo/nested" + sep));

  // With ~username syntax it should return one match if there is an exact
  // match.  It shouldn't translate to the actual directory, it should keep the
  // form the user typed.
  Count = CommandCompletions::DiskDirectories("~Lars", Results, Resolver);
  EXPECT_EQ(Count, Results.GetSize());
  EXPECT_THAT(toVector(Results), UnorderedElementsAre("~Lars" + sep));

  // But with a username that is not found, no results are returned.
  Count = CommandCompletions::DiskDirectories("~Dave", Results, Resolver);
  EXPECT_EQ(Count, Results.GetSize());
  EXPECT_THAT(toVector(Results), UnorderedElementsAre());

  // And if there are multiple matches, it should return all of them.
  Count = CommandCompletions::DiskDirectories("~La", Results, Resolver);
  EXPECT_EQ(Count, Results.GetSize());
  EXPECT_THAT(toVector(Results),
              UnorderedElementsAre("~Lars" + sep, "~Larry" + sep));
}
<commit_msg>Add more pre-run asserts for the DirCompletionAbsolute test<commit_after>//===-- TestCompletion.cpp --------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lldb/Interpreter/CommandCompletions.h"
#include "lldb/Utility/StringList.h"
#include "lldb/Utility/TildeExpressionResolver.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

#include "TestingSupport/MockTildeExpressionResolver.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"

namespace fs = llvm::sys::fs;
namespace path = llvm::sys::path;
using namespace llvm;
using namespace lldb_private;

#define ASSERT_NO_ERROR(x)                                                     \
  if (std::error_code ASSERT_NO_ERROR_ec = x) {                                \
    SmallString<128> MessageStorage;                                           \
    raw_svector_ostream Message(MessageStorage);                               \
    Message << #x ": did not return errc::success.\n"                          \
            << "error number: " << ASSERT_NO_ERROR_ec.value() << "\n"          \
            << "error message: " << ASSERT_NO_ERROR_ec.message() << "\n";      \
    GTEST_FATAL_FAILURE_(MessageStorage.c_str());                              \
  } else {                                                                     \
  }

namespace {

class CompletionTest : public testing::Test {
protected:
  /// Unique temporary directory in which all created filesystem entities must
  /// be placed. It is removed at the end of the test suite.
  SmallString<128> BaseDir;

  /// The working directory that we got when starting the test. Every test
  /// should chdir into this directory first because some tests maybe chdir
  /// into another one during their run.
  static SmallString<128> OriginalWorkingDir;

  SmallString<128> DirFoo;
  SmallString<128> DirFooA;
  SmallString<128> DirFooB;
  SmallString<128> DirFooC;
  SmallString<128> DirBar;
  SmallString<128> DirBaz;
  SmallString<128> DirTestFolder;
  SmallString<128> DirNested;

  SmallString<128> FileAA;
  SmallString<128> FileAB;
  SmallString<128> FileAC;
  SmallString<128> FileFoo;
  SmallString<128> FileBar;
  SmallString<128> FileBaz;

  void SetUp() override {
    // chdir back into the original working dir this test binary started with.
    // A previous test may have have changed the working dir.
    ASSERT_NO_ERROR(fs::set_current_path(OriginalWorkingDir));

    // Get the name of the current test. To prevent that by chance two tests
    // get the same temporary directory if createUniqueDirectory fails.
    auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
    ASSERT_TRUE(test_info != nullptr);
    std::string name = test_info->name();
    ASSERT_NO_ERROR(fs::createUniqueDirectory("FsCompletion-" + name, BaseDir));

    const char *DirNames[] = {"foo", "fooa", "foob",        "fooc",
                              "bar", "baz",  "test_folder", "foo/nested"};
    const char *FileNames[] = {"aa1234.tmp",  "ab1234.tmp",  "ac1234.tmp",
                               "foo1234.tmp", "bar1234.tmp", "baz1234.tmp"};
    SmallString<128> *Dirs[] = {&DirFoo, &DirFooA, &DirFooB,       &DirFooC,
                                &DirBar, &DirBaz,  &DirTestFolder, &DirNested};
    for (auto Dir : llvm::zip(DirNames, Dirs)) {
      auto &Path = *std::get<1>(Dir);
      Path = BaseDir;
      path::append(Path, std::get<0>(Dir));
      ASSERT_NO_ERROR(fs::create_directories(Path));
    }

    SmallString<128> *Files[] = {&FileAA,  &FileAB,  &FileAC,
                                 &FileFoo, &FileBar, &FileBaz};
    for (auto File : llvm::zip(FileNames, Files)) {
      auto &Path = *std::get<1>(File);
      Path = BaseDir;
      path::append(Path, std::get<0>(File));
      int FD;
      ASSERT_NO_ERROR(fs::createUniqueFile(Path, FD, Path));
      ::close(FD);
    }
  }

  static void SetUpTestCase() {
    ASSERT_NO_ERROR(fs::current_path(OriginalWorkingDir));
  }

  void TearDown() override { ASSERT_NO_ERROR(fs::remove_directories(BaseDir)); }

  static bool HasEquivalentFile(const Twine &Path, const StringList &Paths) {
    for (size_t I = 0; I < Paths.GetSize(); ++I) {
      if (fs::equivalent(Path, Paths[I]))
        return true;
    }
    return false;
  }

  void DoDirCompletions(const Twine &Prefix,
                        StandardTildeExpressionResolver &Resolver,
                        StringList &Results) {
    // When a partial name matches, it returns all matches.  If it matches both
    // a full name AND some partial names, it returns all of them.
    uint32_t Count =
        CommandCompletions::DiskDirectories(Prefix + "foo", Results, Resolver);
    ASSERT_EQ(4u, Count);
    ASSERT_EQ(Count, Results.GetSize());
    EXPECT_TRUE(HasEquivalentFile(DirFoo, Results));
    EXPECT_TRUE(HasEquivalentFile(DirFooA, Results));
    EXPECT_TRUE(HasEquivalentFile(DirFooB, Results));
    EXPECT_TRUE(HasEquivalentFile(DirFooC, Results));

    // If it matches only partial names, it still works as expected.
    Count = CommandCompletions::DiskDirectories(Twine(Prefix) + "b", Results,
                                                Resolver);
    ASSERT_EQ(2u, Count);
    ASSERT_EQ(Count, Results.GetSize());
    EXPECT_TRUE(HasEquivalentFile(DirBar, Results));
    EXPECT_TRUE(HasEquivalentFile(DirBaz, Results));
  }
};

SmallString<128> CompletionTest::OriginalWorkingDir;
}

static std::vector<std::string> toVector(const StringList &SL) {
  std::vector<std::string> Result;
  for (size_t Idx = 0; Idx < SL.GetSize(); ++Idx)
    Result.push_back(SL[Idx]);
  return Result;
}
using testing::UnorderedElementsAre;

TEST_F(CompletionTest, DirCompletionAbsolute) {
  // All calls to DiskDirectories() return only directories, even when
  // there are files which also match.  The tests below all check this
  // by asserting an exact result count, and verifying against known
  // folders.

  std::string Prefixes[] = {(Twine(BaseDir) + "/").str(), ""};

  StandardTildeExpressionResolver Resolver;
  StringList Results;

  // When a directory is specified that doesn't end in a slash, it searches
  // for that directory, not items under it.
  // Sanity check that the path we complete on exists and isn't too long.
  ASSERT_TRUE(llvm::sys::fs::exists(BaseDir));
  ASSERT_LE(BaseDir.size(), static_cast<size_t>(PATH_MAX));
  size_t Count =
      CommandCompletions::DiskDirectories(BaseDir, Results, Resolver);
  ASSERT_EQ(1u, Count);
  ASSERT_EQ(Count, Results.GetSize());
  EXPECT_TRUE(HasEquivalentFile(BaseDir, Results));

  // When the same directory ends with a slash, it finds all children.
  Count = CommandCompletions::DiskDirectories(Prefixes[0], Results, Resolver);
  ASSERT_EQ(7u, Count);
  ASSERT_EQ(Count, Results.GetSize());
  EXPECT_TRUE(HasEquivalentFile(DirFoo, Results));
  EXPECT_TRUE(HasEquivalentFile(DirFooA, Results));
  EXPECT_TRUE(HasEquivalentFile(DirFooB, Results));
  EXPECT_TRUE(HasEquivalentFile(DirFooC, Results));
  EXPECT_TRUE(HasEquivalentFile(DirBar, Results));
  EXPECT_TRUE(HasEquivalentFile(DirBaz, Results));
  EXPECT_TRUE(HasEquivalentFile(DirTestFolder, Results));

  DoDirCompletions(Twine(BaseDir) + "/", Resolver, Results);
  llvm::sys::fs::set_current_path(BaseDir);
  DoDirCompletions("", Resolver, Results);
}

TEST_F(CompletionTest, FileCompletionAbsolute) {
  // All calls to DiskFiles() return both files and directories  The tests below
  // all check this by asserting an exact result count, and verifying against
  // known folders.

  StandardTildeExpressionResolver Resolver;
  StringList Results;
  // When an item is specified that doesn't end in a slash but exactly matches
  // one item, it returns that item.
  size_t Count = CommandCompletions::DiskFiles(Twine(BaseDir) + "/fooa",
                                               Results, Resolver);
  ASSERT_EQ(1u, Count);
  ASSERT_EQ(Count, Results.GetSize());
  EXPECT_TRUE(HasEquivalentFile(DirFooA, Results));

  // The previous check verified a directory match.  But it should work for
  // files too.
  Count =
      CommandCompletions::DiskFiles(Twine(BaseDir) + "/aa", Results, Resolver);
  ASSERT_EQ(1u, Count);
  ASSERT_EQ(Count, Results.GetSize());
  EXPECT_TRUE(HasEquivalentFile(FileAA, Results));

  // When it ends with a slash, it should find all files and directories.
  Count =
      CommandCompletions::DiskFiles(Twine(BaseDir) + "/", Results, Resolver);
  ASSERT_EQ(13u, Count);
  ASSERT_EQ(Count, Results.GetSize());
  EXPECT_TRUE(HasEquivalentFile(DirFoo, Results));
  EXPECT_TRUE(HasEquivalentFile(DirFooA, Results));
  EXPECT_TRUE(HasEquivalentFile(DirFooB, Results));
  EXPECT_TRUE(HasEquivalentFile(DirFooC, Results));
  EXPECT_TRUE(HasEquivalentFile(DirBar, Results));
  EXPECT_TRUE(HasEquivalentFile(DirBaz, Results));
  EXPECT_TRUE(HasEquivalentFile(DirTestFolder, Results));

  EXPECT_TRUE(HasEquivalentFile(FileAA, Results));
  EXPECT_TRUE(HasEquivalentFile(FileAB, Results));
  EXPECT_TRUE(HasEquivalentFile(FileAC, Results));
  EXPECT_TRUE(HasEquivalentFile(FileFoo, Results));
  EXPECT_TRUE(HasEquivalentFile(FileBar, Results));
  EXPECT_TRUE(HasEquivalentFile(FileBaz, Results));

  // When a partial name matches, it returns all file & directory matches.
  Count =
      CommandCompletions::DiskFiles(Twine(BaseDir) + "/foo", Results, Resolver);
  ASSERT_EQ(5u, Count);
  ASSERT_EQ(Count, Results.GetSize());
  EXPECT_TRUE(HasEquivalentFile(DirFoo, Results));
  EXPECT_TRUE(HasEquivalentFile(DirFooA, Results));
  EXPECT_TRUE(HasEquivalentFile(DirFooB, Results));
  EXPECT_TRUE(HasEquivalentFile(DirFooC, Results));
  EXPECT_TRUE(HasEquivalentFile(FileFoo, Results));
}

TEST_F(CompletionTest, DirCompletionUsername) {
  MockTildeExpressionResolver Resolver("James", BaseDir);
  Resolver.AddKnownUser("Kirk", DirFooB);
  Resolver.AddKnownUser("Lars", DirFooC);
  Resolver.AddKnownUser("Jason", DirFoo);
  Resolver.AddKnownUser("Larry", DirFooA);
  std::string sep = path::get_separator();

  // Just resolving current user's home directory by itself should return the
  // directory.
  StringList Results;
  size_t Count = CommandCompletions::DiskDirectories("~", Results, Resolver);
  EXPECT_EQ(Count, Results.GetSize());
  EXPECT_THAT(toVector(Results), UnorderedElementsAre("~" + sep));

  // With a slash appended, it should return all items in the directory.
  Count = CommandCompletions::DiskDirectories("~/", Results, Resolver);
  EXPECT_THAT(toVector(Results),
              UnorderedElementsAre(
                  "~/foo" + sep, "~/fooa" + sep, "~/foob" + sep, "~/fooc" + sep,
                  "~/bar" + sep, "~/baz" + sep, "~/test_folder" + sep));
  EXPECT_EQ(Count, Results.GetSize());

  // Check that we can complete directories in nested paths
  Count = CommandCompletions::DiskDirectories("~/foo/", Results, Resolver);
  EXPECT_EQ(Count, Results.GetSize());
  EXPECT_THAT(toVector(Results), UnorderedElementsAre("~/foo/nested" + sep));

  Count = CommandCompletions::DiskDirectories("~/foo/nes", Results, Resolver);
  EXPECT_EQ(Count, Results.GetSize());
  EXPECT_THAT(toVector(Results), UnorderedElementsAre("~/foo/nested" + sep));

  // With ~username syntax it should return one match if there is an exact
  // match.  It shouldn't translate to the actual directory, it should keep the
  // form the user typed.
  Count = CommandCompletions::DiskDirectories("~Lars", Results, Resolver);
  EXPECT_EQ(Count, Results.GetSize());
  EXPECT_THAT(toVector(Results), UnorderedElementsAre("~Lars" + sep));

  // But with a username that is not found, no results are returned.
  Count = CommandCompletions::DiskDirectories("~Dave", Results, Resolver);
  EXPECT_EQ(Count, Results.GetSize());
  EXPECT_THAT(toVector(Results), UnorderedElementsAre());

  // And if there are multiple matches, it should return all of them.
  Count = CommandCompletions::DiskDirectories("~La", Results, Resolver);
  EXPECT_EQ(Count, Results.GetSize());
  EXPECT_THAT(toVector(Results),
              UnorderedElementsAre("~Lars" + sep, "~Larry" + sep));
}
<|endoftext|>


<commit_before>#include "CommonFoundation.h"
#include "CommonMeshUtilities.h"
#include "CommonLog.h"
#include "CommonProfiler.h"

bool isAlembicMeshValid( Alembic::AbcGeom::IObject *pIObj ) {
	//ESS_PROFILE_FUNC();
	Alembic::AbcGeom::IPolyMesh objMesh;
	Alembic::AbcGeom::ISubD objSubD;

	if(Alembic::AbcGeom::IPolyMesh::matches((*pIObj).getMetaData())) {
		objMesh = Alembic::AbcGeom::IPolyMesh(*pIObj,Alembic::Abc::kWrapExisting);
	}
	else {
		objSubD = Alembic::AbcGeom::ISubD(*pIObj,Alembic::Abc::kWrapExisting);
	}

	if(!objMesh.valid() && !objSubD.valid()) {
		return false;
	}
	return true;
}

bool isAlembicMeshNormals( Alembic::AbcGeom::IObject *pIObj, bool& isConstant ) {
	//ESS_PROFILE_FUNC();
	Alembic::AbcGeom::IPolyMesh objMesh;
	Alembic::AbcGeom::ISubD objSubD;

	if(Alembic::AbcGeom::IPolyMesh::matches((*pIObj).getMetaData())) {
		objMesh = Alembic::AbcGeom::IPolyMesh(*pIObj,Alembic::Abc::kWrapExisting);
       if( objMesh.getSchema().getNormalsParam().valid() ) {
			isConstant = objMesh.getSchema().getNormalsParam().isConstant();
			return true;
		}
	}
	else {
		objSubD = Alembic::AbcGeom::ISubD(*pIObj,Alembic::Abc::kWrapExisting);
	}

	isConstant = true;
	return false;
}

bool isAlembicMeshPositions( Alembic::AbcGeom::IObject *pIObj, bool& isConstant ) {
	ESS_PROFILE_SCOPE("isAlembicMeshPositions");
	Alembic::AbcGeom::IPolyMesh objMesh;
	Alembic::AbcGeom::ISubD objSubD;

	if(Alembic::AbcGeom::IPolyMesh::matches((*pIObj).getMetaData())) {
		objMesh = Alembic::AbcGeom::IPolyMesh(*pIObj,Alembic::Abc::kWrapExisting);
		isConstant = objMesh.getSchema().getPositionsProperty().isConstant();
		return true;
	}
	else {
		objSubD = Alembic::AbcGeom::ISubD(*pIObj,Alembic::Abc::kWrapExisting);
		isConstant = objSubD.getSchema().getPositionsProperty().isConstant();
		return true;
	}
	isConstant = true;
	return false;
}

bool isAlembicMeshUVWs( Alembic::AbcGeom::IObject *pIObj, bool& isConstant ) {
	ESS_PROFILE_SCOPE("isAlembicMeshUVWs");
	Alembic::AbcGeom::IPolyMesh objMesh;
	Alembic::AbcGeom::ISubD objSubD;

	if(Alembic::AbcGeom::IPolyMesh::matches((*pIObj).getMetaData())) {
		objMesh = Alembic::AbcGeom::IPolyMesh(*pIObj,Alembic::Abc::kWrapExisting);
		if( objMesh.getSchema().getUVsParam().valid() ) {
			isConstant = objMesh.getSchema().getUVsParam().isConstant();
			return true;
		}
	}
	else {
		objSubD = Alembic::AbcGeom::ISubD(*pIObj,Alembic::Abc::kWrapExisting);
		if( objSubD.getSchema().getUVsParam().valid() ) {
			isConstant = objSubD.getSchema().getUVsParam().isConstant();
			return true;
		}
	}
	isConstant = true;

	return false;
}

bool isAlembicMeshTopoDynamic( Alembic::AbcGeom::IObject *pIObj ) {
	ESS_PROFILE_SCOPE("isAlembicMeshTopoDynamic");
	Alembic::AbcGeom::IPolyMesh objMesh;
	Alembic::AbcGeom::ISubD objSubD;

	if(Alembic::AbcGeom::IPolyMesh::matches((*pIObj).getMetaData())) {
		objMesh = Alembic::AbcGeom::IPolyMesh(*pIObj,Alembic::Abc::kWrapExisting);
	}
	else {
		objSubD = Alembic::AbcGeom::ISubD(*pIObj,Alembic::Abc::kWrapExisting);
	}

	Alembic::AbcGeom::IPolyMeshSchema::Sample polyMeshSample;
	Alembic::AbcGeom::ISubDSchema::Sample subDSample;

	bool hasDynamicTopo = false;
	if(objMesh.valid())
	{
		Alembic::Abc::IInt32ArrayProperty faceCountProp = Alembic::Abc::IInt32ArrayProperty(objMesh.getSchema(),".faceCounts");
		if(faceCountProp.valid()) {
			hasDynamicTopo = !faceCountProp.isConstant();
		}
	}
	else
	{
		Alembic::Abc::IInt32ArrayProperty faceCountProp = Alembic::Abc::IInt32ArrayProperty(objSubD.getSchema(),".faceCounts");
		if(faceCountProp.valid()) {
			hasDynamicTopo = !faceCountProp.isConstant();
		}
	}
	return hasDynamicTopo;
}

bool isAlembicMeshPointCache( Alembic::AbcGeom::IObject *pIObj ) {
	ESS_PROFILE_SCOPE("isAlembicMeshPointCache");
	Alembic::AbcGeom::IPolyMesh objMesh;
	Alembic::AbcGeom::ISubD objSubD;

	if(Alembic::AbcGeom::IPolyMesh::matches((*pIObj).getMetaData())) {
		objMesh = Alembic::AbcGeom::IPolyMesh(*pIObj,Alembic::Abc::kWrapExisting);
	}
	else {
		objSubD = Alembic::AbcGeom::ISubD(*pIObj,Alembic::Abc::kWrapExisting);
	}

	Alembic::AbcGeom::IPolyMeshSchema::Sample polyMeshSample;
	Alembic::AbcGeom::ISubDSchema::Sample subDSample;

	bool isPointCache = false;
	if(objMesh.valid())
	{
		Alembic::Abc::IInt32ArrayProperty faceCountProp = Alembic::Abc::IInt32ArrayProperty(objMesh.getSchema(),".faceCounts");
		if( ! faceCountProp.valid() ) {
			Alembic::Abc::IP3fArrayProperty positionsProp = Alembic::Abc::IP3fArrayProperty(objMesh.getSchema(),".P");
			if( positionsProp.valid() && positionsProp.getValue()->size() > 0 ) {
				isPointCache = true;
			}
		}
		else if( faceCountProp.isConstant() ) {
			Alembic::Abc::Int32ArraySamplePtr faceCounts = faceCountProp.getValue();
			// the first is our preferred method, the second check here is Helge's method that is now considered obsolete
			if( faceCounts->size() == 0 || ( faceCounts->size() == 1 && ( faceCounts->get()[0] == 0 ) ) ) {
				Alembic::Abc::IP3fArrayProperty positionsProp = Alembic::Abc::IP3fArrayProperty(objMesh.getSchema(),".P");
				if( positionsProp.valid() && positionsProp.getValue()->size() > 0 ) {
					isPointCache = true;
				}
			}
		}
	}
	else
	{
		Alembic::Abc::IInt32ArrayProperty faceCountProp = Alembic::Abc::IInt32ArrayProperty(objSubD.getSchema(),".faceCounts");
		if( ! faceCountProp.valid() ) {
			Alembic::Abc::IP3fArrayProperty positionsProp = Alembic::Abc::IP3fArrayProperty(objMesh.getSchema(),".P");
			if( positionsProp.valid() && positionsProp.getValue()->size() > 0 ) {
				isPointCache = true;
			}
		}
		else if( faceCountProp.isConstant() ) {
			Alembic::Abc::Int32ArraySamplePtr faceCounts = faceCountProp.getValue();
			// the first is our preferred method, the second check here is Helge's method that is now considered obsolete
			if( faceCounts->size() == 0 || ( faceCounts->size() == 1 && ( faceCounts->get()[0] == 0 ) ) ) {
				Alembic::Abc::IP3fArrayProperty positionsProp = Alembic::Abc::IP3fArrayProperty(objSubD.getSchema(),".P");
				if( positionsProp.valid() && positionsProp.getValue()->size() > 0 ) {
					isPointCache = true;
				}
			}
		}
	}
	return isPointCache;
}

struct edge
{
	int a;
	int b;

	bool operator<( const edge& rEdge ) const
	{
		if(a < rEdge.a) return true;
		if(a > rEdge.a) return false;
		if(b < rEdge.b) return true;
		return false;
	}
};

struct edgeData
{
	bool bReversed;
	int count;
	int face;

	edgeData():bReversed(false), count(1), face(-1)
	{}

};

int validateAlembicMeshTopo(std::vector<Alembic::AbcCoreAbstract::ALEMBIC_VERSION_NS::int32_t> faceCounts,
							std::vector<Alembic::AbcCoreAbstract::ALEMBIC_VERSION_NS::int32_t> faceIndices,
							const std::string& meshName)
{

	std::map<edge, edgeData> edgeToCountMap;
	typedef std::map<edge, edgeData>::iterator iterator;

	int meshErrors = 0;

	int faceOffset = 0;
	for(int i=0; i<faceCounts.size(); i++){
		int count = faceCounts[i];

		std::vector<int> occurences;
		occurences.reserve(count);

		Alembic::AbcCoreAbstract::ALEMBIC_VERSION_NS::int32_t* v = &faceIndices[faceOffset];
		for(int j=0; j<count; j++){

			occurences.push_back(v[j]);
		}
		std::sort(occurences.begin(), occurences.end());

		for(int j=0; j<occurences.size()-1; j++){

			if(occurences[j] == occurences[j+1]){
				ESS_LOG_WARNING("Error in mesh \""<<meshName<<"\". Vertex "<<occurences[j]<<" of face "<<i<<" is duplicated.");
				meshErrors ++;
			}
		}

		for(int j=0; j<count-1; j++){
			edge e;
			e.a = v[j];
			e.b = v[j+1];

			if( e.a == e.b ){
				//the duplicate vertices check already covers this case.
				//ESS_LOG_WARNING("edge ("<<e.a<<", "<<e.b<<") is degenerate.");
				continue;
			}

			edgeData eData;
			eData.face = i;

			if(e.b < e.a){
				std::swap(e.a, e.b);
				eData.bReversed = true;
			}

			if( edgeToCountMap.find(e) == edgeToCountMap.end() ){
				edgeToCountMap[e] = eData;
			}
			else{
				edgeData eData2 = edgeToCountMap[e];
				eData2.count++;

				if( (eData.bReversed && eData2.bReversed) || (!eData.bReversed && !eData2.bReversed) ){

					ESS_LOG_WARNING("Error in mesh \""<<meshName<<"\". Edge ("<<e.a<<", "<<e.b<<") is shared between polygons "<<eData.face<<" and "<<eData2.face<<", and these polygons have reversed orderings.");
					meshErrors ++;
				}
			}
		}

		faceOffset += count;
	}
	return meshErrors;

}<commit_msg>fixed #23<commit_after>#include "CommonFoundation.h"
#include "CommonMeshUtilities.h"
#include "CommonLog.h"
#include "CommonProfiler.h"

bool isAlembicMeshValid( Alembic::AbcGeom::IObject *pIObj ) {
	//ESS_PROFILE_FUNC();
	Alembic::AbcGeom::IPolyMesh objMesh;
	Alembic::AbcGeom::ISubD objSubD;

	if(Alembic::AbcGeom::IPolyMesh::matches((*pIObj).getMetaData())) {
		objMesh = Alembic::AbcGeom::IPolyMesh(*pIObj,Alembic::Abc::kWrapExisting);
	}
	else {
		objSubD = Alembic::AbcGeom::ISubD(*pIObj,Alembic::Abc::kWrapExisting);
	}

	if(!objMesh.valid() && !objSubD.valid()) {
		return false;
	}
	return true;
}

bool isAlembicMeshNormals( Alembic::AbcGeom::IObject *pIObj, bool& isConstant ) {
	//ESS_PROFILE_FUNC();
	Alembic::AbcGeom::IPolyMesh objMesh;
	Alembic::AbcGeom::ISubD objSubD;

	if(Alembic::AbcGeom::IPolyMesh::matches((*pIObj).getMetaData())) {
		objMesh = Alembic::AbcGeom::IPolyMesh(*pIObj,Alembic::Abc::kWrapExisting);
 		if( objMesh.valid() ) {
			if( objMesh.getSchema().getNormalsParam().valid() ) {
				isConstant = objMesh.getSchema().getNormalsParam().isConstant();
				return true;
			}
		}
	}

	isConstant = true;
	return false;
}

bool isAlembicMeshPositions( Alembic::AbcGeom::IObject *pIObj, bool& isConstant ) {
	ESS_PROFILE_SCOPE("isAlembicMeshPositions");
	Alembic::AbcGeom::IPolyMesh objMesh;
	Alembic::AbcGeom::ISubD objSubD;

	if(Alembic::AbcGeom::IPolyMesh::matches((*pIObj).getMetaData())) {
		objMesh = Alembic::AbcGeom::IPolyMesh(*pIObj,Alembic::Abc::kWrapExisting);
		if( objMesh.valid() ) {
			isConstant = objMesh.getSchema().getPositionsProperty().isConstant();
			return true;
		}
	}
	else {
		objSubD = Alembic::AbcGeom::ISubD(*pIObj,Alembic::Abc::kWrapExisting);
		if( objSubD.valid() ) {
			isConstant = objSubD.getSchema().getPositionsProperty().isConstant();
			return true;
		}
	}
	isConstant = true;
	return false;
}

bool isAlembicMeshUVWs( Alembic::AbcGeom::IObject *pIObj, bool& isConstant ) {
	ESS_PROFILE_SCOPE("isAlembicMeshUVWs");
	Alembic::AbcGeom::IPolyMesh objMesh;
	Alembic::AbcGeom::ISubD objSubD;

	if(Alembic::AbcGeom::IPolyMesh::matches((*pIObj).getMetaData())) {
		objMesh = Alembic::AbcGeom::IPolyMesh(*pIObj,Alembic::Abc::kWrapExisting);
		if( objMesh.valid() ) {
			if( objMesh.getSchema().getUVsParam().valid() ) {
				isConstant = objMesh.getSchema().getUVsParam().isConstant();
				return true;
			}
		}
	}
	else {
		objSubD = Alembic::AbcGeom::ISubD(*pIObj,Alembic::Abc::kWrapExisting);
		if( objSubD.valid() ) {
			if( objSubD.getSchema().getUVsParam().valid() ) {
				isConstant = objSubD.getSchema().getUVsParam().isConstant();
				return true;
			}
		}
	}
	isConstant = true;

	return false;
}

bool isAlembicMeshTopoDynamic( Alembic::AbcGeom::IObject *pIObj ) {
	ESS_PROFILE_SCOPE("isAlembicMeshTopoDynamic");
	Alembic::AbcGeom::IPolyMesh objMesh;
	Alembic::AbcGeom::ISubD objSubD;

	if(Alembic::AbcGeom::IPolyMesh::matches((*pIObj).getMetaData())) {
		objMesh = Alembic::AbcGeom::IPolyMesh(*pIObj,Alembic::Abc::kWrapExisting);
	}
	else {
		objSubD = Alembic::AbcGeom::ISubD(*pIObj,Alembic::Abc::kWrapExisting);
	}

	Alembic::AbcGeom::IPolyMeshSchema::Sample polyMeshSample;
	Alembic::AbcGeom::ISubDSchema::Sample subDSample;

	bool hasDynamicTopo = false;
	if(objMesh.valid())
	{
		Alembic::Abc::IInt32ArrayProperty faceCountProp = Alembic::Abc::IInt32ArrayProperty(objMesh.getSchema(),".faceCounts");
		if(faceCountProp.valid()) {
			hasDynamicTopo = !faceCountProp.isConstant();
		}
	}
	else if( subDSample.valid() )
	{
		Alembic::Abc::IInt32ArrayProperty faceCountProp = Alembic::Abc::IInt32ArrayProperty(objSubD.getSchema(),".faceCounts");
		if(faceCountProp.valid()) {
			hasDynamicTopo = !faceCountProp.isConstant();
		}
	}
	return hasDynamicTopo;
}

bool isAlembicMeshPointCache( Alembic::AbcGeom::IObject *pIObj ) {
	ESS_PROFILE_SCOPE("isAlembicMeshPointCache");
	Alembic::AbcGeom::IPolyMesh objMesh;
	Alembic::AbcGeom::ISubD objSubD;

	if(Alembic::AbcGeom::IPolyMesh::matches((*pIObj).getMetaData())) {
		objMesh = Alembic::AbcGeom::IPolyMesh(*pIObj,Alembic::Abc::kWrapExisting);
	}
	else {
		objSubD = Alembic::AbcGeom::ISubD(*pIObj,Alembic::Abc::kWrapExisting);
	}

	bool isPointCache = false;
	if(objMesh.valid())
	{
		Alembic::Abc::IInt32ArrayProperty faceCountProp = Alembic::Abc::IInt32ArrayProperty(objMesh.getSchema(),".faceCounts");
		if( ! faceCountProp.valid() ) {
			Alembic::Abc::IP3fArrayProperty positionsProp = Alembic::Abc::IP3fArrayProperty(objMesh.getSchema(),"P");
			if( positionsProp.valid() && positionsProp.getValue()->size() > 0 ) {
				isPointCache = true;
			}
		}
		else if( faceCountProp.isConstant() ) {
			Alembic::Abc::Int32ArraySamplePtr faceCounts = faceCountProp.getValue();
			// the first is our preferred method, the second check here is Helge's method that is now considered obsolete
			if( faceCounts->size() == 0 || ( faceCounts->size() == 1 && ( faceCounts->get()[0] == 0 ) ) ) {
				Alembic::Abc::IP3fArrayProperty positionsProp = Alembic::Abc::IP3fArrayProperty(objMesh.getSchema(),"P");
				if( positionsProp.valid() && positionsProp.getValue()->size() > 0 ) {
					isPointCache = true;
				}
			}
		}
	}
	else if( objSubD.valid() )
	{
		Alembic::Abc::IInt32ArrayProperty faceCountProp = Alembic::Abc::IInt32ArrayProperty(objSubD.getSchema(),".faceCounts");
		if( ! faceCountProp.valid() ) {
			Alembic::Abc::IP3fArrayProperty positionsProp = Alembic::Abc::IP3fArrayProperty(objSubD.getSchema(),"P");
			if( positionsProp.valid() && positionsProp.getValue()->size() > 0 ) {
				isPointCache = true;
			}
		}
		else if( faceCountProp.isConstant() ) {
			Alembic::Abc::Int32ArraySamplePtr faceCounts = faceCountProp.getValue();
			// the first is our preferred method, the second check here is Helge's method that is now considered obsolete
			if( faceCounts->size() == 0 || ( faceCounts->size() == 1 && ( faceCounts->get()[0] == 0 ) ) ) {
				Alembic::Abc::IP3fArrayProperty positionsProp = Alembic::Abc::IP3fArrayProperty(objSubD.getSchema(),"P");
				if( positionsProp.valid() && positionsProp.getValue()->size() > 0 ) {
					isPointCache = true;
				}
			}
		}
	}
	return isPointCache;
}

struct edge
{
	int a;
	int b;

	bool operator<( const edge& rEdge ) const
	{
		if(a < rEdge.a) return true;
		if(a > rEdge.a) return false;
		if(b < rEdge.b) return true;
		return false;
	}
};

struct edgeData
{
	bool bReversed;
	int count;
	int face;

	edgeData():bReversed(false), count(1), face(-1)
	{}

};

int validateAlembicMeshTopo(std::vector<Alembic::AbcCoreAbstract::ALEMBIC_VERSION_NS::int32_t> faceCounts,
							std::vector<Alembic::AbcCoreAbstract::ALEMBIC_VERSION_NS::int32_t> faceIndices,
							const std::string& meshName)
{

	std::map<edge, edgeData> edgeToCountMap;
	typedef std::map<edge, edgeData>::iterator iterator;

	int meshErrors = 0;

	int faceOffset = 0;
	for(int i=0; i<faceCounts.size(); i++){
		int count = faceCounts[i];

		std::vector<int> occurences;
		occurences.reserve(count);

		Alembic::AbcCoreAbstract::ALEMBIC_VERSION_NS::int32_t* v = &faceIndices[faceOffset];
		for(int j=0; j<count; j++){

			occurences.push_back(v[j]);
		}
		std::sort(occurences.begin(), occurences.end());

		for(int j=0; j<occurences.size()-1; j++){

			if(occurences[j] == occurences[j+1]){
				ESS_LOG_WARNING("Error in mesh \""<<meshName<<"\". Vertex "<<occurences[j]<<" of face "<<i<<" is duplicated.");
				meshErrors ++;
			}
		}

		for(int j=0; j<count-1; j++){
			edge e;
			e.a = v[j];
			e.b = v[j+1];

			if( e.a == e.b ){
				//the duplicate vertices check already covers this case.
				//ESS_LOG_WARNING("edge ("<<e.a<<", "<<e.b<<") is degenerate.");
				continue;
			}

			edgeData eData;
			eData.face = i;

			if(e.b < e.a){
				std::swap(e.a, e.b);
				eData.bReversed = true;
			}

			if( edgeToCountMap.find(e) == edgeToCountMap.end() ){
				edgeToCountMap[e] = eData;
			}
			else{
				edgeData eData2 = edgeToCountMap[e];
				eData2.count++;

				if( (eData.bReversed && eData2.bReversed) || (!eData.bReversed && !eData2.bReversed) ){

					ESS_LOG_WARNING("Error in mesh \""<<meshName<<"\". Edge ("<<e.a<<", "<<e.b<<") is shared between polygons "<<eData.face<<" and "<<eData2.face<<", and these polygons have reversed orderings.");
					meshErrors ++;
				}
			}
		}

		faceOffset += count;
	}
	return meshErrors;

}<|endoftext|>


<commit_before>// Copyright 2016 Alessio Sclocco <a.sclocco@vu.nl>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <iostream>
#include <vector>
#include <ctime>
#include <algorithm>

#include <configuration.hpp>

#include <Dedispersion.hpp>
#include <ArgumentList.hpp>
#include <Observation.hpp>
#include <utils.hpp>
#include <Shifts.hpp>
#include <ReadData.hpp>
#include <utils.hpp>

int main(int argc, char * argv[]) {
  unsigned int padding = 0;
  uint8_t inputBits = 0;
  bool printResults = false;
  uint64_t wrongSamples = 0;
  std::string channelsFile;
  AstroData::Observation observation_c;
  AstroData::Observation observation;

  try {
    isa::utils::ArgumentList args(argc, argv);
    printResults = args.getSwitch("-print_results");
    inputBits = args.getSwitchArgument< unsigned int >("-input_bits");
    padding = args.getSwitchArgument< unsigned int >("-padding");
    channelsFile = args.getSwitchArgument< std::string >("-zapped_channels");
    observation.setNrBeams(args.getSwitchArgument< unsigned int >("-beams"));
    observation.setNrSyntheticBeams(args.getSwitchArgument< unsigned int >("-synthetic_beams"));
    observation.setFrequencyRange(args.getSwitchArgument< unsigned int >("-subbands"), args.getSwitchArgument< unsigned int >("-channels"), args.getSwitchArgument< float >("-min_freq"), args.getSwitchArgument< float >("-channel_bandwidth"));
    observation.setNrSamplesPerBatch(args.getSwitchArgument< unsigned int >("-samples"));
    observation.setDMSubbandingRange(args.getSwitchArgument< unsigned int >("-subbanding_dms"), args.getSwitchArgument< float >("-subbanding_dm_first"), args.getSwitchArgument< float >("-subbanding_dm_step"));
    observation.setDMRange(args.getSwitchArgument< unsigned int >("-dms"), 0.0, args.getSwitchArgument< float >("-dm_step"));
    observation_c = observation;
    observation_c.setDMRange(observation.getNrDMsSubbanding() * observation.getNrDMs(), args.getSwitchArgument< float >("-dm_first"), observation.getDMStep());
  } catch  ( isa::utils::SwitchNotFound & err ) {
    std::cerr << err.what() << std::endl;
    return 1;
  } catch ( std::exception & err ) {
    std::cerr << "Usage: " << argv[0] << " [-print_results] -input_bits ... -padding ... -zapped_channels ... -beams ... -synthetic_beams ... -min_freq ... -channel_bandwidth ... -samples ... -subbands ... -channels ... -subbanding_dms ... -dms ... -subbanding_dm_first ... -dm_first ... -subbanding_dm_step ... -dm_step ..." << std::endl;
    return 1;
  }

  // Allocate memory
  std::vector< inputDataType > dispersedData;
  std::vector< outputDataType > subbandedData;
  std::vector< outputDataType > dedispersedData;
  std::vector< outputDataType > dedispersedData_c;
  std::vector< uint8_t > zappedChannels(observation_c.getNrPaddedChannels(padding / sizeof(uint8_t)));
  std::vector< uint8_t > beamDriver(observation_c.getNrSyntheticBeams() * observation_c.getNrPaddedChannels(padding / sizeof(uint8_t)));
  std::vector< float > * shifts = PulsarSearch::getShifts(observation_c, padding);

  AstroData::readZappedChannels(observation_c, channelsFile, zappedChannels);
  observation_c.setNrSamplesPerDispersedChannel(observation.getNrSamplesPerBatch() + static_cast< unsigned int >(shifts->at(0) * (observation_c.getFirstDM() + ((observation_c.getNrDMs() - 1) * observation_c.getDMStep()))));
  observation.setNrSamplesPerBatchSubbanding(observation.getNrSamplesPerBatch() + static_cast< unsigned int >(shifts->at(0) * (observation.getFirstDM() + ((observation.getNrDMs() - 1) * observation.getDMStep()))));
  observation.setNrSamplesPerSubbandingDispersedChannel(observation.getNrSamplesPerBatchSubbanding() + static_cast< unsigned int >(shifts->at(0) * (observation.getFirstDMSubbanding() + ((observation.getNrDMsSubbanding() - 1) * observation.getDMSubbandingStep()))));
  if ( inputBits >= 8 ) {
    dispersedData.resize(observation_c.getNrBeams() * observation_c.getNrChannels() * observation_c.getNrSamplesPerPaddedSubbandingDispersedChannel(padding / sizeof(inputDataType)));
    subbandedData.resize(observation.getNrBeams() * observation.getNrSubbands() * observation.getNrDMsSubbanding()  * observation.getNrSamplesPerPaddedSubbandingDispersedChannel(padding / sizeof(inputDataType)));
    dedispersedData.resize(observation.getNrSyntheticBeams() * (observation.getNrDMsSubbanding() * observation.getNrDMs()) * observation.getNrSamplesPerPaddedBatch(padding / sizeof(inputDataType)));
    dedispersedData_c.resize(observation_c.getNrSyntheticBeams() * observation_c.getNrDMs() * observation_c.getNrSamplesPerPaddedBatch(padding / sizeof(inputDataType)));
  } else {
    dispersedData.resize(observation_c.getNrBeams() * observation_c.getNrChannels() * isa::utils::pad(observation_c.getNrSamplesPerDispersedChannel() / (8 / inputBits), padding / sizeof(inputDataType)));
    subbandedData.resize(observation.getNrBeams() * observation.getNrSubbands() * isa::utils::pad(observation.getNrSamplesPerSubbandingDispersedChannel() / (8 / inputBits), padding / sizeof(inputDataType)));
    dedispersedData.resize(observation.getNrSyntheticBeams() * (observation.getNrDMsSubbanding() * observation.getNrDMs()) * isa::utils::pad(observation.getNrSamplesPerBatch() / (8 / inputBits), padding / sizeof(inputDataType)));
    dedispersedData_c.resize(observation_c.getNrSyntheticBeams() * observation_c.getNrDMs() * isa::utils::pad(observation_c.getNrSamplesPerBatch() / (8 / inputBits), padding / sizeof(inputDataType)));
  }

  // Generate data
  srand(time(0));
  for ( unsigned int beam = 0; beam < observation_c.getNrBeams(); beam++ ) {
    for ( unsigned int channel = 0; channel < observation_c.getNrChannels(); channel++ ) {
      for ( unsigned int sample = 0; sample < observation_c.getNrSamplesPerDispersedChannel(); sample++ ) {
        if ( inputBits >= 8 ) {
          dispersedData[(beam * observation_c.getNrChannels() * observation_c.getNrSamplesPerPaddedDispersedChannel(padding / sizeof(inputDataType))) + (channel * observation_c.getNrSamplesPerPaddedDispersedChannel(padding / sizeof(inputDataType))) + sample] = rand() % observation_c.getNrChannels();
        } else {
          unsigned int byte = 0;
          uint8_t firstBit = 0;
          uint8_t value = rand() % inputBits;
          uint8_t buffer = 0;

          byte = sample / (8 / inputBits);
          firstBit = (sample % (8 / inputBits)) * inputBits;
          buffer = dispersedData[(beam * observation_c.getNrChannels() * isa::utils::pad(observation_c.getNrSamplesPerDispersedChannel() / (8 / inputBits), padding / sizeof(inputDataType))) + (channel * isa::utils::pad(observation_c.getNrSamplesPerDispersedChannel() / (8 / inputBits), padding / sizeof(inputDataType))) + byte];
          for ( unsigned int bit = 0; bit < inputBits; bit++ ) {
            isa::utils::setBit(buffer, isa::utils::getBit(value, bit), firstBit + bit);
          }
          dispersedData[(beam * observation_c.getNrChannels() * isa::utils::pad(observation_c.getNrSamplesPerDispersedChannel() / (8 / inputBits), padding / sizeof(inputDataType))) + (channel * isa::utils::pad(observation_c.getNrSamplesPerDispersedChannel() / (8 / inputBits), padding / sizeof(inputDataType))) + byte] = buffer;
        }
      }
    }
  }
  for ( unsigned int beam = 0; beam < observation_c.getNrSyntheticBeams(); beam++ ) {
    for ( unsigned int channel = 0; channel < observation_c.getNrChannels(); channel++ ) {
      beamDriver[(beam * observation_c.getNrPaddedChannels(padding / sizeof(uint8_t))) + channel] = rand() % observation_c.getNrBeams();
    }
  }
  std::fill(subbandedData.begin(), subbandedData.end(), 0);
  std::fill(dedispersedData.begin(), dedispersedData.end(), 0);
  std::fill(dedispersedData_c.begin(), dedispersedData_c.end(), 0);

  // Execute dedispersion
  PulsarSearch::dedispersion< inputDataType, intermediateDataType, outputDataType >(observation_c, zappedChannels, beamDriver, dispersedData, dedispersedData_c, *shifts, padding, inputBits);
  PulsarSearch::subbandDedispersionStepOne< inputDataType, intermediateDataType, outputDataType >(observation, zappedChannels, dispersedData, subbandedData, *shifts, padding, inputBits);
  PulsarSearch::subbandDedispersionStepTwo< outputDataType, intermediateDataType, outputDataType >(observation, beamDriver, subbandedData, dedispersedData, *shifts, padding);

  for ( unsigned int sBeam = 0; sBeam < observation.getNrSyntheticBeams(); sBeam++ ) {
    for ( unsigned int firstStepDM = 0; firstStepDM < observation.getNrDMsSubbanding(); firstStepDM++ ) {
      for ( unsigned int dm = 0; dm < observation.getNrDMs(); dm++ ) {
        if ( printResults ) {
          std::cout << "sBeam: " << sBeam << " DM: " << (firstStepDM * observation.getNrDMs()) + dm << std::endl;
        }
        for ( unsigned int sample = 0; sample < observation.getNrSamplesPerBatch(); sample++ ) {
          if ( !isa::utils::same(dedispersedData[(sBeam * observation.getNrDMsSubbanding() * observation.getNrDMs() * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + (firstStepDM * observation.getNrDMs() * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + (dm * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + sample], dedispersedData_c[(sBeam * observation_c.getNrDMs() * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + (((firstStepDM * observation.getNrDMs()) + dm) * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + sample]) ) {
            wrongSamples++;
          }
          if ( printResults ) {
            std::cout << dedispersedData[(sBeam * observation.getNrDMsSubbanding() * observation.getNrDMs() * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + (firstStepDM * observation.getNrDMs() * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + (dm * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + sample] << "," << dedispersedData_c[(sBeam * observation_c.getNrDMs() * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + (((firstStepDM * observation.getNrDMs()) + dm) * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + sample] << " ";
          }
        }
        if ( printResults ) {
          std::cout << std::endl;
        }
      }
    }
  }

  if ( wrongSamples > 0 ) {
    std::cout << "Wrong samples: " << wrongSamples << " (" << (wrongSamples * 100.0) / (static_cast< uint64_t >(observation_c.getNrSyntheticBeams()) * observation_c.getNrDMs() * observation_c.getNrSamplesPerBatch()) << "%)." << std::endl;
  } else {
    std::cout << "TEST PASSED." << std::endl;
  }

  return 0;
}

<commit_msg>Fixed the < 8 bit case.<commit_after>// Copyright 2016 Alessio Sclocco <a.sclocco@vu.nl>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <iostream>
#include <vector>
#include <ctime>
#include <algorithm>

#include <configuration.hpp>

#include <Dedispersion.hpp>
#include <ArgumentList.hpp>
#include <Observation.hpp>
#include <utils.hpp>
#include <Shifts.hpp>
#include <ReadData.hpp>
#include <utils.hpp>

int main(int argc, char * argv[]) {
  unsigned int padding = 0;
  uint8_t inputBits = 0;
  bool printResults = false;
  uint64_t wrongSamples = 0;
  std::string channelsFile;
  AstroData::Observation observation_c;
  AstroData::Observation observation;

  try {
    isa::utils::ArgumentList args(argc, argv);
    printResults = args.getSwitch("-print_results");
    inputBits = args.getSwitchArgument< unsigned int >("-input_bits");
    padding = args.getSwitchArgument< unsigned int >("-padding");
    channelsFile = args.getSwitchArgument< std::string >("-zapped_channels");
    observation.setNrBeams(args.getSwitchArgument< unsigned int >("-beams"));
    observation.setNrSyntheticBeams(args.getSwitchArgument< unsigned int >("-synthetic_beams"));
    observation.setFrequencyRange(args.getSwitchArgument< unsigned int >("-subbands"), args.getSwitchArgument< unsigned int >("-channels"), args.getSwitchArgument< float >("-min_freq"), args.getSwitchArgument< float >("-channel_bandwidth"));
    observation.setNrSamplesPerBatch(args.getSwitchArgument< unsigned int >("-samples"));
    observation.setDMSubbandingRange(args.getSwitchArgument< unsigned int >("-subbanding_dms"), args.getSwitchArgument< float >("-subbanding_dm_first"), args.getSwitchArgument< float >("-subbanding_dm_step"));
    observation.setDMRange(args.getSwitchArgument< unsigned int >("-dms"), 0.0, args.getSwitchArgument< float >("-dm_step"));
    observation_c = observation;
    observation_c.setDMRange(observation.getNrDMsSubbanding() * observation.getNrDMs(), args.getSwitchArgument< float >("-dm_first"), observation.getDMStep());
  } catch  ( isa::utils::SwitchNotFound & err ) {
    std::cerr << err.what() << std::endl;
    return 1;
  } catch ( std::exception & err ) {
    std::cerr << "Usage: " << argv[0] << " [-print_results] -input_bits ... -padding ... -zapped_channels ... -beams ... -synthetic_beams ... -min_freq ... -channel_bandwidth ... -samples ... -subbands ... -channels ... -subbanding_dms ... -dms ... -subbanding_dm_first ... -dm_first ... -subbanding_dm_step ... -dm_step ..." << std::endl;
    return 1;
  }

  // Allocate memory
  std::vector< inputDataType > dispersedData;
  std::vector< outputDataType > subbandedData;
  std::vector< outputDataType > dedispersedData;
  std::vector< outputDataType > dedispersedData_c;
  std::vector< uint8_t > zappedChannels(observation_c.getNrPaddedChannels(padding / sizeof(uint8_t)));
  std::vector< uint8_t > beamDriver(observation_c.getNrSyntheticBeams() * observation_c.getNrPaddedChannels(padding / sizeof(uint8_t)));
  std::vector< float > * shifts = PulsarSearch::getShifts(observation_c, padding);

  AstroData::readZappedChannels(observation_c, channelsFile, zappedChannels);
  observation_c.setNrSamplesPerDispersedChannel(observation.getNrSamplesPerBatch() + static_cast< unsigned int >(shifts->at(0) * (observation_c.getFirstDM() + ((observation_c.getNrDMs() - 1) * observation_c.getDMStep()))));
  observation.setNrSamplesPerBatchSubbanding(observation.getNrSamplesPerBatch() + static_cast< unsigned int >(shifts->at(0) * (observation.getFirstDM() + ((observation.getNrDMs() - 1) * observation.getDMStep()))));
  observation.setNrSamplesPerSubbandingDispersedChannel(observation.getNrSamplesPerBatchSubbanding() + static_cast< unsigned int >(shifts->at(0) * (observation.getFirstDMSubbanding() + ((observation.getNrDMsSubbanding() - 1) * observation.getDMSubbandingStep()))));
  if ( inputBits >= 8 ) {
    dispersedData.resize(observation_c.getNrBeams() * observation_c.getNrChannels() * observation_c.getNrSamplesPerPaddedSubbandingDispersedChannel(padding / sizeof(inputDataType)));
    subbandedData.resize(observation.getNrBeams() * observation.getNrSubbands() * observation.getNrDMsSubbanding()  * observation.getNrSamplesPerPaddedSubbandingDispersedChannel(padding / sizeof(inputDataType)));
    dedispersedData.resize(observation.getNrSyntheticBeams() * (observation.getNrDMsSubbanding() * observation.getNrDMs()) * observation.getNrSamplesPerPaddedBatch(padding / sizeof(inputDataType)));
    dedispersedData_c.resize(observation_c.getNrSyntheticBeams() * observation_c.getNrDMs() * observation_c.getNrSamplesPerPaddedBatch(padding / sizeof(inputDataType)));
  } else {
    dispersedData.resize(observation_c.getNrBeams() * observation_c.getNrChannels() * isa::utils::pad(observation_c.getNrSamplesPerPaddedDispersedChannel() / (8 / inputBits), padding / sizeof(inputDataType)));
    subbandedData.resize(observation.getNrBeams() * observation.getNrSubbands() * isa::utils::pad(observation.getNrSamplesPerSubbandingDispersedChannel() / (8 / inputBits), padding / sizeof(inputDataType)));
    dedispersedData.resize(observation.getNrSyntheticBeams() * (observation.getNrDMsSubbanding() * observation.getNrDMs()) * isa::utils::pad(observation.getNrSamplesPerBatch() / (8 / inputBits), padding / sizeof(inputDataType)));
    dedispersedData_c.resize(observation_c.getNrSyntheticBeams() * observation_c.getNrDMs() * isa::utils::pad(observation_c.getNrSamplesPerBatch() / (8 / inputBits), padding / sizeof(inputDataType)));
  }

  // Generate data
  srand(time(0));
  for ( unsigned int beam = 0; beam < observation_c.getNrBeams(); beam++ ) {
    for ( unsigned int channel = 0; channel < observation_c.getNrChannels(); channel++ ) {
      for ( unsigned int sample = 0; sample < observation_c.getNrSamplesPerDispersedChannel(); sample++ ) {
        if ( inputBits >= 8 ) {
          dispersedData[(beam * observation_c.getNrChannels() * observation_c.getNrSamplesPerPaddedDispersedChannel(padding / sizeof(inputDataType))) + (channel * observation_c.getNrSamplesPerPaddedDispersedChannel(padding / sizeof(inputDataType))) + sample] = rand() % observation_c.getNrChannels();
        } else {
          unsigned int byte = 0;
          uint8_t firstBit = 0;
          uint8_t value = rand() % inputBits;
          uint8_t buffer = 0;

          byte = sample / (8 / inputBits);
          firstBit = (sample % (8 / inputBits)) * inputBits;
          buffer = dispersedData[(beam * observation_c.getNrChannels() * isa::utils::pad(observation_c.getNrSamplesPerDispersedChannel() / (8 / inputBits), padding / sizeof(inputDataType))) + (channel * isa::utils::pad(observation_c.getNrSamplesPerDispersedChannel() / (8 / inputBits), padding / sizeof(inputDataType))) + byte];
          for ( unsigned int bit = 0; bit < inputBits; bit++ ) {
            isa::utils::setBit(buffer, isa::utils::getBit(value, bit), firstBit + bit);
          }
          dispersedData[(beam * observation_c.getNrChannels() * isa::utils::pad(observation_c.getNrSamplesPerDispersedChannel() / (8 / inputBits), padding / sizeof(inputDataType))) + (channel * isa::utils::pad(observation_c.getNrSamplesPerDispersedChannel() / (8 / inputBits), padding / sizeof(inputDataType))) + byte] = buffer;
        }
      }
    }
  }
  for ( unsigned int beam = 0; beam < observation_c.getNrSyntheticBeams(); beam++ ) {
    for ( unsigned int channel = 0; channel < observation_c.getNrChannels(); channel++ ) {
      beamDriver[(beam * observation_c.getNrPaddedChannels(padding / sizeof(uint8_t))) + channel] = rand() % observation_c.getNrBeams();
    }
  }
  std::fill(subbandedData.begin(), subbandedData.end(), 0);
  std::fill(dedispersedData.begin(), dedispersedData.end(), 0);
  std::fill(dedispersedData_c.begin(), dedispersedData_c.end(), 0);

  // Execute dedispersion
  PulsarSearch::dedispersion< inputDataType, intermediateDataType, outputDataType >(observation_c, zappedChannels, beamDriver, dispersedData, dedispersedData_c, *shifts, padding, inputBits);
  PulsarSearch::subbandDedispersionStepOne< inputDataType, intermediateDataType, outputDataType >(observation, zappedChannels, dispersedData, subbandedData, *shifts, padding, inputBits);
  PulsarSearch::subbandDedispersionStepTwo< outputDataType, intermediateDataType, outputDataType >(observation, beamDriver, subbandedData, dedispersedData, *shifts, padding);

  for ( unsigned int sBeam = 0; sBeam < observation.getNrSyntheticBeams(); sBeam++ ) {
    for ( unsigned int firstStepDM = 0; firstStepDM < observation.getNrDMsSubbanding(); firstStepDM++ ) {
      for ( unsigned int dm = 0; dm < observation.getNrDMs(); dm++ ) {
        if ( printResults ) {
          std::cout << "sBeam: " << sBeam << " DM: " << (firstStepDM * observation.getNrDMs()) + dm << std::endl;
        }
        for ( unsigned int sample = 0; sample < observation.getNrSamplesPerBatch(); sample++ ) {
          if ( !isa::utils::same(dedispersedData[(sBeam * observation.getNrDMsSubbanding() * observation.getNrDMs() * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + (firstStepDM * observation.getNrDMs() * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + (dm * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + sample], dedispersedData_c[(sBeam * observation_c.getNrDMs() * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + (((firstStepDM * observation.getNrDMs()) + dm) * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + sample]) ) {
            wrongSamples++;
          }
          if ( printResults ) {
            std::cout << dedispersedData[(sBeam * observation.getNrDMsSubbanding() * observation.getNrDMs() * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + (firstStepDM * observation.getNrDMs() * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + (dm * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + sample] << "," << dedispersedData_c[(sBeam * observation_c.getNrDMs() * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + (((firstStepDM * observation.getNrDMs()) + dm) * observation.getNrSamplesPerPaddedBatch(padding / sizeof(outputDataType))) + sample] << " ";
          }
        }
        if ( printResults ) {
          std::cout << std::endl;
        }
      }
    }
  }

  if ( wrongSamples > 0 ) {
    std::cout << "Wrong samples: " << wrongSamples << " (" << (wrongSamples * 100.0) / (static_cast< uint64_t >(observation_c.getNrSyntheticBeams()) * observation_c.getNrDMs() * observation_c.getNrSamplesPerBatch()) << "%)." << std::endl;
  } else {
    std::cout << "TEST PASSED." << std::endl;
  }

  return 0;
}

<|endoftext|>


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

// Disable exception handler warnings.
#pragma warning( disable : 4530 )

#include <errno.h>

#include "client/windows/sender/crash_report_sender.h"
#include "common/windows/http_upload.h"

#if _MSC_VER < 1400  // MSVC 2005/8
// Older MSVC doesn't have fscanf_s, but they are compatible as long as
// we don't use the string conversions (%s/%c/%S/%C).
#define fscanf_s fscanf
#endif

namespace google_breakpad {

static const char kCheckpointSignature[] = "GBP1\n";

CrashReportSender::CrashReportSender(const wstring &checkpoint_file)
    : checkpoint_file_(checkpoint_file),
      max_reports_per_day_(-1),
      last_sent_date_(-1),
      reports_sent_(0) {
  FILE *fd;
  if (OpenCheckpointFile(L"r", &fd) == 0) {
    ReadCheckpoint(fd);
    fclose(fd);
  }
}

ReportResult CrashReportSender::SendCrashReport(
    const wstring &url, const map<wstring, wstring> &parameters,
    const wstring &dump_file_name, wstring *report_code) {
  int today = GetCurrentDate();
  if (today == last_sent_date_ &&
      max_reports_per_day_ != -1 &&
      reports_sent_ >= max_reports_per_day_) {
    return RESULT_THROTTLED;
  }

  int http_response = 0;
  bool result = HTTPUpload::SendRequest(
    url, parameters, dump_file_name, L"upload_file_minidump", NULL, report_code,
    &http_response);

  if (result) {
    ReportSent(today);
    return RESULT_SUCCEEDED;
  } else if (http_response == 400) {  // TODO: update if/when the server
                                      //       switches to a different code
    return RESULT_REJECTED;
  } else {
    return RESULT_FAILED;
  }
}

void CrashReportSender::ReadCheckpoint(FILE *fd) {
  char buf[128];
  if (!fgets(buf, sizeof(buf), fd) ||
      strcmp(buf, kCheckpointSignature) != 0) {
    return;
  }

  if (fscanf_s(fd, "%d\n", &last_sent_date_) != 1) {
    last_sent_date_ = -1;
    return;
  }
  if (fscanf_s(fd, "%d\n", &reports_sent_) != 1) {
    reports_sent_ = 0;
    return;
  }
}

void CrashReportSender::ReportSent(int today) {
  // Update the report stats
  if (today != last_sent_date_) {
    last_sent_date_ = today;
    reports_sent_ = 0;
  }
  ++reports_sent_;

  // Update the checkpoint file
  FILE *fd;
  if (OpenCheckpointFile(L"w", &fd) == 0) {
    fputs(kCheckpointSignature, fd);
    fprintf(fd, "%d\n", last_sent_date_);
    fprintf(fd, "%d\n", reports_sent_);
    fclose(fd);
  }
}

int CrashReportSender::GetCurrentDate() const {
  SYSTEMTIME system_time;
  GetSystemTime(&system_time);
  return (system_time.wYear * 10000) + (system_time.wMonth * 100) +
      system_time.wDay;
}

int CrashReportSender::OpenCheckpointFile(const wchar_t *mode, FILE **fd) {
  if (checkpoint_file_.empty()) {
    return ENOENT;
  }
#if _MSC_VER >= 1400  // MSVC 2005/8
  return _wfopen_s(fd, checkpoint_file_.c_str(), mode);
#else
  *fd = _wfopen(checkpoint_file_.c_str(), mode);
  if (*fd == NULL) {
    return errno;
  }
  return 0;
#endif
}

}  // namespace google_breakpad
<commit_msg>Actually treat fatal error codes as fatal<commit_after>// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Disable exception handler warnings.
#pragma warning( disable : 4530 )

#include <errno.h>

#include "client/windows/sender/crash_report_sender.h"
#include "common/windows/http_upload.h"

#if _MSC_VER < 1400  // MSVC 2005/8
// Older MSVC doesn't have fscanf_s, but they are compatible as long as
// we don't use the string conversions (%s/%c/%S/%C).
#define fscanf_s fscanf
#endif

namespace google_breakpad {

static const char kCheckpointSignature[] = "GBP1\n";

CrashReportSender::CrashReportSender(const wstring &checkpoint_file)
    : checkpoint_file_(checkpoint_file),
      max_reports_per_day_(-1),
      last_sent_date_(-1),
      reports_sent_(0) {
  FILE *fd;
  if (OpenCheckpointFile(L"r", &fd) == 0) {
    ReadCheckpoint(fd);
    fclose(fd);
  }
}

ReportResult CrashReportSender::SendCrashReport(
    const wstring &url, const map<wstring, wstring> &parameters,
    const wstring &dump_file_name, wstring *report_code) {
  int today = GetCurrentDate();
  if (today == last_sent_date_ &&
      max_reports_per_day_ != -1 &&
      reports_sent_ >= max_reports_per_day_) {
    return RESULT_THROTTLED;
  }

  int http_response = 0;
  bool result = HTTPUpload::SendRequest(
    url, parameters, dump_file_name, L"upload_file_minidump", NULL, report_code,
    &http_response);

  if (result) {
    ReportSent(today);
    return RESULT_SUCCEEDED;
  } else if (http_response >= 400 && http_response < 500) {
    return RESULT_REJECTED;
  } else {
    return RESULT_FAILED;
  }
}

void CrashReportSender::ReadCheckpoint(FILE *fd) {
  char buf[128];
  if (!fgets(buf, sizeof(buf), fd) ||
      strcmp(buf, kCheckpointSignature) != 0) {
    return;
  }

  if (fscanf_s(fd, "%d\n", &last_sent_date_) != 1) {
    last_sent_date_ = -1;
    return;
  }
  if (fscanf_s(fd, "%d\n", &reports_sent_) != 1) {
    reports_sent_ = 0;
    return;
  }
}

void CrashReportSender::ReportSent(int today) {
  // Update the report stats
  if (today != last_sent_date_) {
    last_sent_date_ = today;
    reports_sent_ = 0;
  }
  ++reports_sent_;

  // Update the checkpoint file
  FILE *fd;
  if (OpenCheckpointFile(L"w", &fd) == 0) {
    fputs(kCheckpointSignature, fd);
    fprintf(fd, "%d\n", last_sent_date_);
    fprintf(fd, "%d\n", reports_sent_);
    fclose(fd);
  }
}

int CrashReportSender::GetCurrentDate() const {
  SYSTEMTIME system_time;
  GetSystemTime(&system_time);
  return (system_time.wYear * 10000) + (system_time.wMonth * 100) +
      system_time.wDay;
}

int CrashReportSender::OpenCheckpointFile(const wchar_t *mode, FILE **fd) {
  if (checkpoint_file_.empty()) {
    return ENOENT;
  }
#if _MSC_VER >= 1400  // MSVC 2005/8
  return _wfopen_s(fd, checkpoint_file_.c_str(), mode);
#else
  *fd = _wfopen(checkpoint_file_.c_str(), mode);
  if (*fd == NULL) {
    return errno;
  }
  return 0;
#endif
}

}  // namespace google_breakpad
<|endoftext|>