<commit_before>/*
  Copyright (c) 2012 The WebM project authors. All Rights Reserved.

  Use of this source code is governed by a BSD-style license
  that can be found in the LICENSE file in the root of the source
  tree. An additional intellectual property rights grant can be found
  in the file PATENTS.  All contributing project authors may
  be found in the AUTHORS file in the root of the source tree.
*/

#include "third_party/googletest/src/include/gtest/gtest.h"
#include "test/codec_factory.h"
#include "test/encode_test_driver.h"
#include "test/i420_video_source.h"
#include "test/util.h"

namespace {

const int kMaxErrorFrames = 8;
const int kMaxDroppableFrames = 8;

class ErrorResilienceTest : public ::libvpx_test::EncoderTest,
    public ::libvpx_test::CodecTestWithParam<libvpx_test::TestMode> {
 protected:
  ErrorResilienceTest() : EncoderTest(GET_PARAM(0)),
                          psnr_(0.0),
                          nframes_(0),
                          mismatch_psnr_(0.0),
                          mismatch_nframes_(0),
                          encoding_mode_(GET_PARAM(1)) {
    Reset();
  }

  virtual ~ErrorResilienceTest() {}

  void Reset() {
    error_nframes_ = 0;
    droppable_nframes_ = 0;
  }

  virtual void SetUp() {
    InitializeConfig();
    SetMode(encoding_mode_);
  }

  virtual void BeginPassHook(unsigned int /*pass*/) {
    psnr_ = 0.0;
    nframes_ = 0;
    mismatch_psnr_ = 0.0;
    mismatch_nframes_ = 0;
  }

  virtual bool Continue() const {
    return !HasFatalFailure() && !abort_;
  }

  virtual void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) {
    psnr_ += pkt->data.psnr.psnr[0];
    nframes_++;
  }

  virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video) {
    frame_flags_ &= ~(VP8_EFLAG_NO_UPD_LAST |
                      VP8_EFLAG_NO_UPD_GF |
                      VP8_EFLAG_NO_UPD_ARF);
    if (droppable_nframes_ > 0 &&
        (cfg_.g_pass == VPX_RC_LAST_PASS || cfg_.g_pass == VPX_RC_ONE_PASS)) {
      for (unsigned int i = 0; i < droppable_nframes_; ++i) {
        if (droppable_frames_[i] == nframes_) {
          std::cout << "             Encoding droppable frame: "
                    << droppable_frames_[i] << "\n";
          frame_flags_ |= (VP8_EFLAG_NO_UPD_LAST |
                           VP8_EFLAG_NO_UPD_GF |
                           VP8_EFLAG_NO_UPD_ARF);
          return;
        }
      }
    }
  }

  double GetAveragePsnr() const {
    if (nframes_)
      return psnr_ / nframes_;
    return 0.0;
  }

  double GetAverageMismatchPsnr() const {
    if (mismatch_nframes_)
      return mismatch_psnr_ / mismatch_nframes_;
    return 0.0;
  }

  virtual bool DoDecode() const {
    if (error_nframes_ > 0 &&
        (cfg_.g_pass == VPX_RC_LAST_PASS || cfg_.g_pass == VPX_RC_ONE_PASS)) {
      for (unsigned int i = 0; i < error_nframes_; ++i) {
        if (error_frames_[i] == nframes_ - 1) {
          std::cout << "             Skipping decoding frame: "
                    << error_frames_[i] << "\n";
          return 0;
        }
      }
    }
    return 1;
  }

  virtual void MismatchHook(const vpx_image_t *img1,
                            const vpx_image_t *img2) {
    double mismatch_psnr = compute_psnr(img1, img2);
    mismatch_psnr_ += mismatch_psnr;
    ++mismatch_nframes_;
    // std::cout << "Mismatch frame psnr: " << mismatch_psnr << "\n";
  }

  void SetErrorFrames(int num, unsigned int *list) {
    if (num > kMaxErrorFrames)
      num = kMaxErrorFrames;
    else if (num < 0)
      num = 0;
    error_nframes_ = num;
    for (unsigned int i = 0; i < error_nframes_; ++i)
      error_frames_[i] = list[i];
  }

  void SetDroppableFrames(int num, unsigned int *list) {
    if (num > kMaxDroppableFrames)
      num = kMaxDroppableFrames;
    else if (num < 0)
      num = 0;
    droppable_nframes_ = num;
    for (unsigned int i = 0; i < droppable_nframes_; ++i)
      droppable_frames_[i] = list[i];
  }

  unsigned int GetMismatchFrames() {
    return mismatch_nframes_;
  }

 private:
  double psnr_;
  unsigned int nframes_;
  unsigned int error_nframes_;
  unsigned int droppable_nframes_;
  double mismatch_psnr_;
  unsigned int mismatch_nframes_;
  unsigned int error_frames_[kMaxErrorFrames];
  unsigned int droppable_frames_[kMaxDroppableFrames];
  libvpx_test::TestMode encoding_mode_;
};

TEST_P(ErrorResilienceTest, OnVersusOff) {
  const vpx_rational timebase = { 33333333, 1000000000 };
  cfg_.g_timebase = timebase;
  cfg_.rc_target_bitrate = 2000;
  cfg_.g_lag_in_frames = 25;

  init_flags_ = VPX_CODEC_USE_PSNR;

  libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
                                     timebase.den, timebase.num, 0, 30);

  // Error resilient mode OFF.
  cfg_.g_error_resilient = 0;
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
  const double psnr_resilience_off = GetAveragePsnr();
  EXPECT_GT(psnr_resilience_off, 25.0);

  // Error resilient mode ON.
  cfg_.g_error_resilient = 1;
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
  const double psnr_resilience_on = GetAveragePsnr();
  EXPECT_GT(psnr_resilience_on, 25.0);

  // Test that turning on error resilient mode hurts by 10% at most.
  if (psnr_resilience_off > 0.0) {
    const double psnr_ratio = psnr_resilience_on / psnr_resilience_off;
    EXPECT_GE(psnr_ratio, 0.9);
    EXPECT_LE(psnr_ratio, 1.1);
  }
}

TEST_P(ErrorResilienceTest, DropFramesWithoutRecovery) {
  const vpx_rational timebase = { 33333333, 1000000000 };
  cfg_.g_timebase = timebase;
  cfg_.rc_target_bitrate = 2000;
  cfg_.g_lag_in_frames = 5;

  init_flags_ = VPX_CODEC_USE_PSNR;

  libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
                                     timebase.den, timebase.num, 0, 30);

  // Error resilient mode ON.
  cfg_.g_error_resilient = 1;

  // Set an arbitrary set of error frames same as droppable frames
  unsigned int num_droppable_frames = 2;
  unsigned int droppable_frame_list[] = {5, 16};
  SetDroppableFrames(num_droppable_frames, droppable_frame_list);
  SetErrorFrames(num_droppable_frames, droppable_frame_list);
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
  // Test that no mismatches have been found
  std::cout << "             Mismatch frames: "
            << GetMismatchFrames() << "\n";
  EXPECT_EQ(GetMismatchFrames(), (unsigned int) 0);

  // reset previously set error/droppable frames
  Reset();

  // Now set an arbitrary set of error frames that are non-droppable
  unsigned int num_error_frames = 3;
  unsigned int error_frame_list[] = {3, 10, 20};
  SetErrorFrames(num_error_frames, error_frame_list);
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
  // Test that dropping an arbitrary set of inter frames does not hurt too much
  // Note the Average Mismatch PSNR is the average of the PSNR between
  // decoded frame and encoder's version of the same frame for all frames
  // with mismatch.
  const double psnr_resilience_mismatch = GetAverageMismatchPsnr();
  std::cout << "             Mismatch PSNR: "
            << psnr_resilience_mismatch << "\n";
  EXPECT_GT(psnr_resilience_mismatch, 20.0);
}

VP8_INSTANTIATE_TEST_CASE(ErrorResilienceTest, ONE_PASS_TEST_MODES);
VP9_INSTANTIATE_TEST_CASE(ErrorResilienceTest, ONE_PASS_TEST_MODES);

}  // namespace
<commit_msg>Tweak DropFramesWithoutRecovery params<commit_after>/*
  Copyright (c) 2012 The WebM project authors. All Rights Reserved.

  Use of this source code is governed by a BSD-style license
  that can be found in the LICENSE file in the root of the source
  tree. An additional intellectual property rights grant can be found
  in the file PATENTS.  All contributing project authors may
  be found in the AUTHORS file in the root of the source tree.
*/

#include "third_party/googletest/src/include/gtest/gtest.h"
#include "test/codec_factory.h"
#include "test/encode_test_driver.h"
#include "test/i420_video_source.h"
#include "test/util.h"

namespace {

const int kMaxErrorFrames = 8;
const int kMaxDroppableFrames = 8;

class ErrorResilienceTest : public ::libvpx_test::EncoderTest,
    public ::libvpx_test::CodecTestWithParam<libvpx_test::TestMode> {
 protected:
  ErrorResilienceTest() : EncoderTest(GET_PARAM(0)),
                          psnr_(0.0),
                          nframes_(0),
                          mismatch_psnr_(0.0),
                          mismatch_nframes_(0),
                          encoding_mode_(GET_PARAM(1)) {
    Reset();
  }

  virtual ~ErrorResilienceTest() {}

  void Reset() {
    error_nframes_ = 0;
    droppable_nframes_ = 0;
  }

  virtual void SetUp() {
    InitializeConfig();
    SetMode(encoding_mode_);
  }

  virtual void BeginPassHook(unsigned int /*pass*/) {
    psnr_ = 0.0;
    nframes_ = 0;
    mismatch_psnr_ = 0.0;
    mismatch_nframes_ = 0;
  }

  virtual bool Continue() const {
    return !HasFatalFailure() && !abort_;
  }

  virtual void PSNRPktHook(const vpx_codec_cx_pkt_t *pkt) {
    psnr_ += pkt->data.psnr.psnr[0];
    nframes_++;
  }

  virtual void PreEncodeFrameHook(libvpx_test::VideoSource *video) {
    frame_flags_ &= ~(VP8_EFLAG_NO_UPD_LAST |
                      VP8_EFLAG_NO_UPD_GF |
                      VP8_EFLAG_NO_UPD_ARF);
    if (droppable_nframes_ > 0 &&
        (cfg_.g_pass == VPX_RC_LAST_PASS || cfg_.g_pass == VPX_RC_ONE_PASS)) {
      for (unsigned int i = 0; i < droppable_nframes_; ++i) {
        if (droppable_frames_[i] == nframes_) {
          std::cout << "             Encoding droppable frame: "
                    << droppable_frames_[i] << "\n";
          frame_flags_ |= (VP8_EFLAG_NO_UPD_LAST |
                           VP8_EFLAG_NO_UPD_GF |
                           VP8_EFLAG_NO_UPD_ARF);
          return;
        }
      }
    }
  }

  double GetAveragePsnr() const {
    if (nframes_)
      return psnr_ / nframes_;
    return 0.0;
  }

  double GetAverageMismatchPsnr() const {
    if (mismatch_nframes_)
      return mismatch_psnr_ / mismatch_nframes_;
    return 0.0;
  }

  virtual bool DoDecode() const {
    if (error_nframes_ > 0 &&
        (cfg_.g_pass == VPX_RC_LAST_PASS || cfg_.g_pass == VPX_RC_ONE_PASS)) {
      for (unsigned int i = 0; i < error_nframes_; ++i) {
        if (error_frames_[i] == nframes_ - 1) {
          std::cout << "             Skipping decoding frame: "
                    << error_frames_[i] << "\n";
          return 0;
        }
      }
    }
    return 1;
  }

  virtual void MismatchHook(const vpx_image_t *img1,
                            const vpx_image_t *img2) {
    double mismatch_psnr = compute_psnr(img1, img2);
    mismatch_psnr_ += mismatch_psnr;
    ++mismatch_nframes_;
    // std::cout << "Mismatch frame psnr: " << mismatch_psnr << "\n";
  }

  void SetErrorFrames(int num, unsigned int *list) {
    if (num > kMaxErrorFrames)
      num = kMaxErrorFrames;
    else if (num < 0)
      num = 0;
    error_nframes_ = num;
    for (unsigned int i = 0; i < error_nframes_; ++i)
      error_frames_[i] = list[i];
  }

  void SetDroppableFrames(int num, unsigned int *list) {
    if (num > kMaxDroppableFrames)
      num = kMaxDroppableFrames;
    else if (num < 0)
      num = 0;
    droppable_nframes_ = num;
    for (unsigned int i = 0; i < droppable_nframes_; ++i)
      droppable_frames_[i] = list[i];
  }

  unsigned int GetMismatchFrames() {
    return mismatch_nframes_;
  }

 private:
  double psnr_;
  unsigned int nframes_;
  unsigned int error_nframes_;
  unsigned int droppable_nframes_;
  double mismatch_psnr_;
  unsigned int mismatch_nframes_;
  unsigned int error_frames_[kMaxErrorFrames];
  unsigned int droppable_frames_[kMaxDroppableFrames];
  libvpx_test::TestMode encoding_mode_;
};

TEST_P(ErrorResilienceTest, OnVersusOff) {
  const vpx_rational timebase = { 33333333, 1000000000 };
  cfg_.g_timebase = timebase;
  cfg_.rc_target_bitrate = 2000;
  cfg_.g_lag_in_frames = 25;

  init_flags_ = VPX_CODEC_USE_PSNR;

  libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
                                     timebase.den, timebase.num, 0, 30);

  // Error resilient mode OFF.
  cfg_.g_error_resilient = 0;
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
  const double psnr_resilience_off = GetAveragePsnr();
  EXPECT_GT(psnr_resilience_off, 25.0);

  // Error resilient mode ON.
  cfg_.g_error_resilient = 1;
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
  const double psnr_resilience_on = GetAveragePsnr();
  EXPECT_GT(psnr_resilience_on, 25.0);

  // Test that turning on error resilient mode hurts by 10% at most.
  if (psnr_resilience_off > 0.0) {
    const double psnr_ratio = psnr_resilience_on / psnr_resilience_off;
    EXPECT_GE(psnr_ratio, 0.9);
    EXPECT_LE(psnr_ratio, 1.1);
  }
}

TEST_P(ErrorResilienceTest, DropFramesWithoutRecovery) {
  const vpx_rational timebase = { 33333333, 1000000000 };
  cfg_.g_timebase = timebase;
  cfg_.rc_target_bitrate = 500;

  init_flags_ = VPX_CODEC_USE_PSNR;

  libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288,
                                     timebase.den, timebase.num, 0, 30);

  // Error resilient mode ON.
  cfg_.g_error_resilient = 1;

  // Set an arbitrary set of error frames same as droppable frames
  unsigned int num_droppable_frames = 2;
  unsigned int droppable_frame_list[] = {5, 16};
  SetDroppableFrames(num_droppable_frames, droppable_frame_list);
  SetErrorFrames(num_droppable_frames, droppable_frame_list);
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
  // Test that no mismatches have been found
  std::cout << "             Mismatch frames: "
            << GetMismatchFrames() << "\n";
  EXPECT_EQ(GetMismatchFrames(), (unsigned int) 0);

  // reset previously set error/droppable frames
  Reset();

  // Now set an arbitrary set of error frames that are non-droppable
  unsigned int num_error_frames = 3;
  unsigned int error_frame_list[] = {3, 10, 20};
  SetErrorFrames(num_error_frames, error_frame_list);
  ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
  // Test that dropping an arbitrary set of inter frames does not hurt too much
  // Note the Average Mismatch PSNR is the average of the PSNR between
  // decoded frame and encoder's version of the same frame for all frames
  // with mismatch.
  const double psnr_resilience_mismatch = GetAverageMismatchPsnr();
  std::cout << "             Mismatch PSNR: "
            << psnr_resilience_mismatch << "\n";
  EXPECT_GT(psnr_resilience_mismatch, 20.0);
}

VP8_INSTANTIATE_TEST_CASE(ErrorResilienceTest, ONE_PASS_TEST_MODES);
VP9_INSTANTIATE_TEST_CASE(ErrorResilienceTest, ONE_PASS_TEST_MODES);

}  // namespace
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2019.  All rights reserved
 */
#ifndef _Stroika_Foundation_IO_Network_Transfer_Client_libcurl_inl_
#define _Stroika_Foundation_IO_Network_Transfer_Client_libcurl_inl_ 1

/*
 ********************************************************************************
 ***************************** Implementation Details ***************************
 ********************************************************************************
 */
CompileTimeFlagChecker_HEADER (Stroika::Foundation::IO::Network::Transfer, qHasFeature_LibCurl, qHasFeature_LibCurl);

namespace Stroika::Foundation::IO::Network::Transfer {

#if qHasFeature_LibCurl
    class LibCurlException [[deprecated ("Since v2.1d18, use SystemErrorException{ hr, LibCurl_error_category () }")]] : public Execution::SystemErrorException<>
    {
    public:
        using CURLcode = int; // tried directly to reference libcurl CURLcode but tricky cuz its an enum -- LGP 2012-05-08
    public:
        LibCurlException (CURLcode ccode);

    public:
        // throw Exception () type iff the status indicates a real exception code. This MAY not throw an exception of type LibCurlException,
        // but MAY map to any other exception type
        static void ThrowIfError (CURLcode status);

    public:
        nonvirtual CURLcode GetCode () const
        {
            return fCurlCode_;
        }

    private:
        CURLcode fCurlCode_;
    };
#endif

}

#endif /*_Stroika_Foundation_IO_Network_Transfer_Client_libcurl_inl_*/
<commit_msg>deprecated class typo<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2019.  All rights reserved
 */
#ifndef _Stroika_Foundation_IO_Network_Transfer_Client_libcurl_inl_
#define _Stroika_Foundation_IO_Network_Transfer_Client_libcurl_inl_ 1

/*
 ********************************************************************************
 ***************************** Implementation Details ***************************
 ********************************************************************************
 */
CompileTimeFlagChecker_HEADER (Stroika::Foundation::IO::Network::Transfer, qHasFeature_LibCurl, qHasFeature_LibCurl);

namespace Stroika::Foundation::IO::Network::Transfer {

#if qHasFeature_LibCurl
    class [[deprecated ("Since v2.1d18, use SystemErrorException{ hr, LibCurl_error_category () }")]]  LibCurlException : public Execution::SystemErrorException<>
    {
    public:
        using CURLcode = int; // tried directly to reference libcurl CURLcode but tricky cuz its an enum -- LGP 2012-05-08
    public:
        LibCurlException (CURLcode ccode);

    public:
        // throw Exception () type iff the status indicates a real exception code. This MAY not throw an exception of type LibCurlException,
        // but MAY map to any other exception type
        static void ThrowIfError (CURLcode status);

    public:
        nonvirtual CURLcode GetCode () const
        {
            return fCurlCode_;
        }

    private:
        CURLcode fCurlCode_;
    };
#endif

}

#endif /*_Stroika_Foundation_IO_Network_Transfer_Client_libcurl_inl_*/
<|endoftext|>


<commit_before>#include "catch.hpp"

#include <iostream> // std::cout, std::endl
#include <exception> // std::bad_function_call, std::runtime_error
#include <thread> // std::thread, std::this_thread::yield
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
#include <functional> // std::function

/**
 * The EventThreader class runs two threads simutaniously but not at the same
 * time. The switching between threads is controlled by the EventThreader
 * class. The two threads are the current thread (named the calling thread)
 * and the second thread is spawned from a given function (event thread). The
 * event thread is spawned and paused in the construction of the EventThreader
 * class.
 *
 * Operation
 * Only the calling thread will run until it requests that event thread is run.
 * While one thread is running, it is guaranteed that the other is paused. On
 * EventThreader construction, the event thead is paused. The event thread is
 * invoked (and the calling thread paused) either with calls
 * EventThreader::join() or EventThreader::switchToEventThread(). From the
 * event thread, the calling thread can be invoked by a call to the event
 * threads function parameter.
 *
 * The EventThreader takes one parameter on construction to be a functional
 * object f of type void(void(void)). f will be the function run as the event
 * thread. f's parameter is the function that invokes the event thread to
 * pause, allowing the calling thread to continue.
 *
 * Every call to EventThreader::switchToEventThread() from the calling thread
 * must be followed by a call EventThreader::switchToCallingThread() via
 * calling the event theads functional parameter from the event thread.
 */
class EventThreader {
private:
    std::condition_variable event_waiter, calling_waiter;
    std::unique_lock<std::mutex>* event_lock, * calling_lock;
    std::mutex mtx;
    std::thread event_thread;
    void switchToCallingThread();
    bool request_end_of_event = false;
    bool waiting_for_event = false;
    std::function<void(void)> event_cleanup;
    std::runtime_error* exception_from_the_event_thread;
public:
    EventThreader(std::function<void (std::function<void (void)>)> func);
    ~EventThreader();
    void switchToEventThread();
    void join();
    void setEventCleanup(std::function<void(void)>);
};

EventThreader::EventThreader(std::function<void (std::function<void (void)>)> func) {
    exception_from_the_event_thread = nullptr;
    calling_lock = new std::unique_lock<std::mutex>(mtx);
    event_cleanup = [](){}; // empty function
    auto event = [&](){
        /* mtx force switch to calling - blocked by the mutex */
        event_lock = new std::unique_lock<std::mutex>(mtx);
        calling_waiter.notify_one();
        event_waiter.wait(*event_lock);
        std::this_thread::yield();
        try {
            func([&](){switchToCallingThread();});
            if (waiting_for_event) { // the event has ended, but not ready to join
                // rejoin the calling thread after dealing with this exception
                calling_waiter.notify_one();
                throw std::runtime_error("calling thread is not ready to join");
            }
        } catch (const std::runtime_error &e) {
            /* report the exception to the calling thread */
            exception_from_the_event_thread = new std::runtime_error(e);
        }
        delete event_lock;
        event_cleanup();
    };

    event_thread = std::thread(event);
    std::this_thread::yield();
    calling_waiter.wait(*calling_lock);
    std::this_thread::yield();
}

EventThreader::~EventThreader() {}

void EventThreader::switchToCallingThread() {
    if (request_end_of_event) {
        std::runtime_error err("calling thread requested join, cannot call switchToCallingThread()");
        throw err;
    }
    /* switch to calling */
    calling_waiter.notify_one();
    std::this_thread::yield();
    event_waiter.wait(*event_lock);
    std::this_thread::yield();
    /* back from calling */
}

void EventThreader::switchToEventThread() {
    waiting_for_event = true;
    /* switch to event */
    event_waiter.notify_one();
    std::this_thread::yield();
    calling_waiter.wait(*calling_lock);
    std::this_thread::yield();
    /* back from event */
    waiting_for_event = false;
}

void EventThreader::join() {
    request_end_of_event = true;
    delete calling_lock; // remove lock on this thread, allow event to run
    event_waiter.notify_one();
    std::this_thread::yield();
    event_thread.join();
    if (exception_from_the_event_thread != nullptr) {
        /* an exception occured */
        std::runtime_error e_copy(exception_from_the_event_thread->what());
        delete exception_from_the_event_thread;
        throw e_copy;
    }
}

void EventThreader::setEventCleanup(std::function<void(void)> cleanup) {
    event_cleanup = cleanup;
}

TEST_CASE( "EventThreader", "[EventThreader]" ) {
	std::stringstream ss;

	SECTION("Abitrary use") {
		/* Example of most basic use */
		auto f = [&ss](std::function<void(void)> switchToMainThread){
			for(int i = 0; i < 100; i++) { ss << "*"; }
			switchToMainThread();
			for(int i = 0; i < 50; i++) { ss << "*"; }
			switchToMainThread();
		};
		EventThreader et(f);
		et.switchToEventThread();
		for(int i = 0; i < 75; i++) { ss << "$"; }
		et.switchToEventThread();
		for(int i = 0; i < 25; i++) { ss << "$"; }
		et.join();

		/* Generate what the result should look like */
		std::string requirement;
		for(int i = 0; i < 100; i++) { requirement += "*"; }
		for(int i = 0; i < 75; i++) { requirement += "$"; }
		for(int i = 0; i < 50; i++) { requirement += "*"; }
		for(int i = 0; i < 25; i++) { requirement += "$"; }
		REQUIRE( requirement == ss.str());

	}

	SECTION("Empty event - no switch, most basic use") {
		auto f = [&ss](std::function<void(void)> switchToMainThread){};
		EventThreader et(f);
		et.join();
		REQUIRE( ss.str() == "" );
	}

	SECTION("Empty event - no switch, main prints a thing") {
		auto f = [&ss](std::function<void(void)> switchToMainThread){};
		EventThreader et(f);
		ss << "here";
		et.join();
		REQUIRE( ss.str() == "here" );
	}

	SECTION("no switch to event") {
		auto f = [&ss](std::function<void(void)> switchToMainThread){
			ss << "f";
		};
		EventThreader et(f);
		et.join();
		REQUIRE( ss.str() == "f" );
	}

	SECTION("one switch to event - trigger exception with type std::runtime_error") {
		REQUIRE_THROWS_AS([&ss](){
			auto f = [&ss](std::function<void(void)> switchToMainThread){
				ss << "f";
			};
			EventThreader et(f);
			et.switchToEventThread();
			et.join();
		}, std::runtime_error);
		REQUIRE( ss.str() == "f" );
	}

	SECTION("one switch to event - trigger exception with text") {
		REQUIRE_THROWS_WITH([&ss](){
			auto f = [&ss](std::function<void(void)> switchToMainThread){
				ss << "f";
			};
			EventThreader et(f);
			et.switchToEventThread();
			et.join();
		}, "calling thread is not ready to join");
	}

	SECTION("two switch to event - trigger exception with type std::runtime_error") {
		REQUIRE_THROWS_AS([&ss](){
			auto f = [&ss](std::function<void(void)> switchToMainThread){
				ss << "f";
			};
			EventThreader et(f);
			et.switchToEventThread();
			et.switchToEventThread();
			et.join();
		}, std::runtime_error);
		REQUIRE( ss.str() == "f" );
	}

	SECTION("two switch to event - trigger exception with text") {
		REQUIRE_THROWS_WITH([&ss](){
			auto f = [&ss](std::function<void(void)> switchToMainThread){
				ss << "f";
			};
			EventThreader et(f);
			et.switchToEventThread();
			et.switchToEventThread();
			et.join();
		}, "calling thread is not ready to join");
	}

	SECTION("print order") {
		auto f = [&ss](std::function<void(void)> switchToMainThread){
			ss << 1;
			switchToMainThread();
			ss << 3;
			switchToMainThread();
			ss << 5;
			switchToMainThread();
		};
		EventThreader et(f);
		ss << 0;
		et.switchToEventThread();
		ss << 2;
		et.switchToEventThread();
		ss << 4;
		et.switchToEventThread();
		ss << 6;
		et.join();
		REQUIRE( ss.str() == "0123456" );
	}

	SECTION("no event switch to main") {
		auto f = [&ss](std::function<void(void)> switchToMainThread){
			ss << "1";
			ss << "2";
		};
		EventThreader et(f);
		et.join();
		REQUIRE( ss.str() == "12" );
	}

	SECTION("one event switch to main") {
		auto f = [&ss](std::function<void(void)> switchToMainThread){
			ss << "1";
			switchToMainThread();
			ss << "2";
		};
		EventThreader et(f);
		et.switchToEventThread();
		et.join();
		REQUIRE( ss.str() == "1" );
	}

	SECTION("premiture join with exception message") {
		auto f = [&ss](std::function<void(void)> switchToMainThread){
			switchToMainThread();
			switchToMainThread();
		};
		EventThreader et(f);
		et.switchToEventThread();
		REQUIRE_THROWS_WITH([&et](){
			et.join();
		}, "EventThreader attempted join but event thread is still running");
	}

	SECTION("EventThreader when event still running") {
		auto f = [&ss](std::function<void(void)> switchToMainThread){
			switchToMainThread();
			switchToMainThread();
		};
		EventThreader* et = new EventThreader(f);
		et->switchToEventThread();
		REQUIRE_THROWS_WITH([&et](){
			delete et;
		}, "EventThreader attempted deconstructor but event thread is still running");
	}
}
<commit_msg>condensed some tests<commit_after>#include "catch.hpp"

#include <iostream> // std::cout, std::endl
#include <exception> // std::bad_function_call, std::runtime_error
#include <thread> // std::thread, std::this_thread::yield
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
#include <functional> // std::function

/**
 * The EventThreader class runs two threads simutaniously but not at the same
 * time. The switching between threads is controlled by the EventThreader
 * class. The two threads are the current thread (named the calling thread)
 * and the second thread is spawned from a given function (event thread). The
 * event thread is spawned and paused in the construction of the EventThreader
 * class.
 *
 * Operation
 * Only the calling thread will run until it requests that event thread is run.
 * While one thread is running, it is guaranteed that the other is paused. On
 * EventThreader construction, the event thead is paused. The event thread is
 * invoked (and the calling thread paused) either with calls
 * EventThreader::join() or EventThreader::switchToEventThread(). From the
 * event thread, the calling thread can be invoked by a call to the event
 * threads function parameter.
 *
 * The EventThreader takes one parameter on construction to be a functional
 * object f of type void(void(void)). f will be the function run as the event
 * thread. f's parameter is the function that invokes the event thread to
 * pause, allowing the calling thread to continue.
 *
 * Every call to EventThreader::switchToEventThread() from the calling thread
 * must be followed by a call EventThreader::switchToCallingThread() via
 * calling the event theads functional parameter from the event thread.
 */
class EventThreader {
private:
    std::condition_variable event_waiter, calling_waiter;
    std::unique_lock<std::mutex>* event_lock, * calling_lock;
    std::mutex mtx;
    std::thread event_thread;
    void switchToCallingThread();
    bool request_end_of_event = false;
    bool waiting_for_event = false;
    bool event_ended = false;
    std::function<void(void)> event_cleanup;
    std::runtime_error* exception_from_the_event_thread;
public:
    EventThreader(std::function<void (std::function<void (void)>)> func);
    ~EventThreader();
    void switchToEventThread();
    void join();
    void setEventCleanup(std::function<void(void)>);
};

EventThreader::EventThreader(std::function<void (std::function<void (void)>)> func) {
    exception_from_the_event_thread = nullptr;
    calling_lock = new std::unique_lock<std::mutex>(mtx);
    event_cleanup = [](){}; // empty function
    auto event = [&](){
        /* mtx force switch to calling - blocked by the mutex */
        event_lock = new std::unique_lock<std::mutex>(mtx);
        calling_waiter.notify_one();
        event_waiter.wait(*event_lock);
        std::this_thread::yield();
        try {
            func([&](){switchToCallingThread();});
            if (waiting_for_event) { // the event has ended, but not ready to join
                // rejoin the calling thread after dealing with this exception
                calling_waiter.notify_one();
                throw std::runtime_error("calling thread is not ready to join");
            }
        } catch (const std::runtime_error &e) {
            /* report the exception to the calling thread */
            exception_from_the_event_thread = new std::runtime_error(e);
        }
        delete event_lock;
        event_cleanup();
        event_ended = true;
    };

    event_thread = std::thread(event);
    std::this_thread::yield();
    calling_waiter.wait(*calling_lock);
    std::this_thread::yield();
}

EventThreader::~EventThreader() {}

void EventThreader::switchToCallingThread() {
    if (request_end_of_event) {
        std::runtime_error err("calling thread requested join, cannot call switchToCallingThread()");
        throw err;
    }
    /* switch to calling */
    calling_waiter.notify_one();
    std::this_thread::yield();
    event_waiter.wait(*event_lock);
    std::this_thread::yield();
    /* back from calling */
}

void EventThreader::switchToEventThread() {
    if (event_ended) {
        throw std::runtime_error("event thread ended, cannot switch to it");
    }
    waiting_for_event = true;
    /* switch to event */
    event_waiter.notify_one();
    std::this_thread::yield();
    calling_waiter.wait(*calling_lock);
    std::this_thread::yield();
    /* back from event */
    waiting_for_event = false;
}

void EventThreader::join() {
    request_end_of_event = true;
    delete calling_lock; // remove lock on this thread, allow event to run
    event_waiter.notify_one();
    std::this_thread::yield();
    event_thread.join();
    if (exception_from_the_event_thread != nullptr) {
        /* an exception occured */
        std::runtime_error e_copy(exception_from_the_event_thread->what());
        delete exception_from_the_event_thread;
        throw e_copy;
    }
}

void EventThreader::setEventCleanup(std::function<void(void)> cleanup) {
    event_cleanup = cleanup;
}

TEST_CASE( "EventThreader", "[EventThreader]" ) {
	std::stringstream ss;

	SECTION("Abitrary use") {
		/* Example of most basic use */
		auto f = [&ss](std::function<void(void)> switchToMainThread){
			for(int i = 0; i < 100; i++) { ss << "*"; }
			switchToMainThread();
			for(int i = 0; i < 50; i++) { ss << "*"; }
			switchToMainThread();
		};
		EventThreader et(f);
		et.switchToEventThread();
		for(int i = 0; i < 75; i++) { ss << "$"; }
		et.switchToEventThread();
		for(int i = 0; i < 25; i++) { ss << "$"; }
		et.join();

		/* Generate what the result should look like */
		std::string requirement;
		for(int i = 0; i < 100; i++) { requirement += "*"; }
		for(int i = 0; i < 75; i++) { requirement += "$"; }
		for(int i = 0; i < 50; i++) { requirement += "*"; }
		for(int i = 0; i < 25; i++) { requirement += "$"; }
		REQUIRE( requirement == ss.str());

	}

	SECTION("Empty event - no switch, most basic use") {
		auto f = [&ss](std::function<void(void)> switchToMainThread){};
		EventThreader et(f);
		et.join();
		REQUIRE( ss.str() == "" );
	}

	SECTION("Empty event - no switch, main prints a thing") {
		auto f = [&ss](std::function<void(void)> switchToMainThread){};
		EventThreader et(f);
		ss << "here";
		et.join();
		REQUIRE( ss.str() == "here" );
	}

	SECTION("event is run without switch") {
		auto f = [&ss](std::function<void(void)> switchToMainThread){
			ss << "f";
		};
		EventThreader et(f);
		et.join();
		REQUIRE( ss.str() == "f" );
	}

	SECTION("event is run at join (not asked to run before)") {
		auto f = [&ss](std::function<void(void)> switchToMainThread){
			ss << "f";
		};
		EventThreader et(f);
		ss << "p";
		et.join();
		REQUIRE( ss.str() == "pf" );
	}

	SECTION("one switch to event without a switch to main") {
		auto f = [&ss](){
			auto f = [&ss](std::function<void(void)> switchToMainThread){
				ss << "f";
			};
			EventThreader et(f);
			et.switchToEventThread();
			et.join();
		};
		REQUIRE_THROWS_AS(f(), std::runtime_error);
		REQUIRE( ss.str() == "f" );
		REQUIRE_THROWS_WITH(f(), "calling thread is not ready to join");
		REQUIRE( ss.str() == "ff" );
	}

	SECTION("two switch to event - trigger exception with type std::runtime_error") {
		REQUIRE_THROWS_AS([&ss](){
			auto f = [&ss](std::function<void(void)> switchToMainThread){
				ss << "f";
			};
			EventThreader et(f);
			et.switchToEventThread();
			et.switchToEventThread();
			et.join();
		}(), std::runtime_error);
		REQUIRE( ss.str() == "f" );
	}

	SECTION("two switch to event - trigger exception with text") {
		REQUIRE_THROWS_WITH([&ss](){
			auto f = [&ss](std::function<void(void)> switchToMainThread){
				ss << "f";
			};
			EventThreader et(f);
			et.switchToEventThread();
			et.switchToEventThread();
			et.join();
		}(), "event thread ended, cannot switch to it");
	}

	SECTION("print order") {
		auto f = [&ss](std::function<void(void)> switchToMainThread){
			ss << 1;
			switchToMainThread();
			ss << 3;
			switchToMainThread();
			ss << 5;
			switchToMainThread();
		};
		EventThreader et(f);
		ss << 0;
		et.switchToEventThread();
		ss << 2;
		et.switchToEventThread();
		ss << 4;
		et.switchToEventThread();
		ss << 6;
		et.join();
		REQUIRE( ss.str() == "0123456" );
	}

	SECTION("no event switch to main") {
		auto f = [&ss](std::function<void(void)> switchToMainThread){
			ss << "1";
			ss << "2";
		};
		EventThreader et(f);
		et.join();
		REQUIRE( ss.str() == "12" );
	}

	SECTION("one event switch to main") {
		auto f = [&ss](std::function<void(void)> switchToMainThread){
			ss << "1";
			switchToMainThread();
			ss << "2";
		};
		EventThreader et(f);
		et.switchToEventThread();
		et.join();
		REQUIRE( ss.str() == "1" );
	}

	SECTION("premiture join with exception message") {
		auto f = [&ss](std::function<void(void)> switchToMainThread){
			switchToMainThread();
			switchToMainThread();
		};
		EventThreader et(f);
		et.switchToEventThread();
		REQUIRE_THROWS_WITH([&et](){
			et.join();
		}, "EventThreader attempted join but event thread is still running");
	}

	SECTION("EventThreader when event still running") {
		auto f = [&ss](std::function<void(void)> switchToMainThread){
			switchToMainThread();
			switchToMainThread();
		};
		EventThreader* et = new EventThreader(f);
		et->switchToEventThread();
		REQUIRE_THROWS_WITH([&et](){
			delete et;
		}(), "EventThreader attempted deconstructor but event thread is still running");
	}
}
<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
#include <string>

// Loriano: let's try Armadillo quick code
#include <armadillo>

#define ENTDIM 8
#define COORDIM (ENTDIM-2)

namespace
{
  int numofline (const char * fname)
  {
    int number_of_lines = 0;
    std::string line;
    std::ifstream myfile(fname);

    while (std::getline(myfile, line))
      ++number_of_lines;

    myfile.close();

    return number_of_lines;
  }
}

int main (int argc, char ** argv)
{
  if (argc != 2)
  {
    std::cerr << "usage: " << argv[0] << " coordinatesfile " << std::endl;
    return 1;
  }

  int num_of_line = numofline(argv[1]);
  std::cout << "file has " << num_of_line << " line " << std::endl;
  int num_of_ent = (num_of_line-1)/ENTDIM;
  std::cout << "  " << num_of_ent << " entries " << std::endl;

  // non perfomante ma easy to go
  double ** param_mtx = new double *[num_of_ent];
  double ** coord_mtx = new double *[num_of_ent];
  for (int i = 0; i < num_of_ent; ++i)
  {
    coord_mtx[i] = new double[3*COORDIM];
    param_mtx[i] = new double[5];
  }

  // leggere file coordinate tracce simulate plus parametri
  std::string line;
  std::ifstream mytfp;
  mytfp.open (argv[1], std::ios::in);

  std::getline (mytfp, line);
  //std::cout << line << std::endl;

  for (int i = 0; i < num_of_ent; ++i)
  {
    int fake1, fake2;
    mytfp >> fake1 >> fake2 ;
#ifdef DEBUG
    std::cout << fake1 << " " << fake2 << std::endl;
#endif
    for (int j = 0; j < COORDIM; ++j)
    {
      int a, b, c;
      mytfp >> coord_mtx[i][j*3] >>
               coord_mtx[i][j*3+1] >>
               coord_mtx[i][j*3+2] >>
               a >> b >> c;
    }
    mytfp >> param_mtx[i][0] >>
             param_mtx[i][1] >>
             param_mtx[i][2] >>
             param_mtx[i][3] >>
             param_mtx[i][4];
  }

  mytfp.close();

#ifdef DEBUG
  for (int i = 0; i < num_of_ent; ++i)
  {
    for (int j = 0; j < COORDIM; ++j)
    {
      std::cout << coord_mtx[i][j*3] << " " <<
                   coord_mtx[i][j*3+1] << " " <<
                   coord_mtx[i][j*3+2] << std::endl;
    }
    std::cout << param_mtx[i][0] << " " <<
                 param_mtx[i][1] << " " <<
                 param_mtx[i][2] << " " <<
                 param_mtx[i][3] << " " <<
                 param_mtx[i][4] << std::endl;
  }
#endif

  double sum = 1.0e0;
  double coordm[3*COORDIM] = {0.0e0};
  double hc[3*COORDIM][3*COORDIM] = {0.0e0};

  for (int l=0; l<num_of_ent; ++l)
  {
    sum += 1.0e0;
    for (int i=0; i<(3*COORDIM); ++i)
      coordm[i] += (coord_mtx[l][i]-coordm[i])/sum;

    for (int i=0; i<(3*COORDIM); ++i)
    {
      for (int j=0; j<(3*COORDIM); ++j)
      {
        hc[i][j] += ((coord_mtx[l][i] - coordm[i])*
                     (coord_mtx[l][j] - coordm[j])-
                     (sum-1.0e0)*hc[i][j]/sum)/(sum-1.0e0);
      }
    }
  }

  for (int i=0; i<(3*COORDIM); ++i)
    for (int j=i+1; j<(3*COORDIM); ++j)
      if (hc[i][j] != hc[j][i])
        std::cout << i << " " << j << " " <<
          hc[i][j] << " ERROR" << std::endl;;

  arma::mat hca = arma::zeros<arma::mat>(3*COORDIM,3*COORDIM);
  for (int i=0; i<(3*COORDIM); ++i)
    for (int j=0; j<(3*COORDIM); ++j)
      hca(i,j) = hc[i][j];

  arma::vec eigval;
  arma::mat eigvec;

  arma::eig_sym(eigval, eigvec, hca);

  double totval = 0.0e0;
  for (int i=0; i<(3*COORDIM); ++i)
    totval += eigval(i);

  int j = 1;
  double totvar = 0.0e0;
  for (int i=(3*COORDIM-1); i>=0; --i)
  {
    if (j <= 5)
      totvar += 100.0e0*(eigval(i)/totval);
    ++j;

    std::cout << i+1 << " ==> " << 100.0e0*(eigval(i)/totval) << std::endl;
  }
  std::cout << "5 eigenvalues: " << totvar << std::endl;

  // documento Annovi
  // calcolo matrice di correlazione traccie HC
  // diagonalizzo HC e determino A, matrice 5 autovettori principali (5 componenti pricipali)
  // A matrice rotazione che mi permette di calcolare la traslazione usando i paamtri di tracce
  // simulate.

  // documento ATLAS
  // calcolare V e data V calcolare inversa V e quindi C, matrice di rotazione
  // data C determinare il vettore di traslazione q
  // c plus q costanti PCA
  // write constants in a file

  for (int i = 0; i < num_of_ent; ++i)
  {
    delete(coord_mtx[i]);
    delete(param_mtx[i]);
  }
  delete(coord_mtx);
  delete(param_mtx);

  return 0;
}
<commit_msg>quick note (i.e. pseudocode like)<commit_after>#include <iostream>
#include <fstream>
#include <string>

// Loriano: let's try Armadillo quick code
#include <armadillo>

#define ENTDIM 8
#define COORDIM (ENTDIM-2)

namespace
{
  int numofline (const char * fname)
  {
    int number_of_lines = 0;
    std::string line;
    std::ifstream myfile(fname);

    while (std::getline(myfile, line))
      ++number_of_lines;

    myfile.close();

    return number_of_lines;
  }
}

int main (int argc, char ** argv)
{
  if (argc != 2)
  {
    std::cerr << "usage: " << argv[0] << " coordinatesfile " << std::endl;
    return 1;
  }

  int num_of_line = numofline(argv[1]);
  std::cout << "file has " << num_of_line << " line " << std::endl;
  int num_of_ent = (num_of_line-1)/ENTDIM;
  std::cout << "  " << num_of_ent << " entries " << std::endl;

  // non perfomante ma easy to go
  double ** param_mtx = new double *[num_of_ent];
  double ** coord_mtx = new double *[num_of_ent];
  for (int i = 0; i < num_of_ent; ++i)
  {
    coord_mtx[i] = new double[3*COORDIM];
    param_mtx[i] = new double[5];
  }

  // leggere file coordinate tracce simulate plus parametri
  std::string line;
  std::ifstream mytfp;
  mytfp.open (argv[1], std::ios::in);

  std::getline (mytfp, line);
  //std::cout << line << std::endl;

  for (int i = 0; i < num_of_ent; ++i)
  {
    int fake1, fake2;
    mytfp >> fake1 >> fake2 ;
#ifdef DEBUG
    std::cout << fake1 << " " << fake2 << std::endl;
#endif
    for (int j = 0; j < COORDIM; ++j)
    {
      int a, b, c;
      mytfp >> coord_mtx[i][j*3] >>
               coord_mtx[i][j*3+1] >>
               coord_mtx[i][j*3+2] >>
               a >> b >> c;
    }
    mytfp >> param_mtx[i][0] >>
             param_mtx[i][1] >>
             param_mtx[i][2] >>
             param_mtx[i][3] >>
             param_mtx[i][4];
  }

  mytfp.close();

#ifdef DEBUG
  for (int i = 0; i < num_of_ent; ++i)
  {
    for (int j = 0; j < COORDIM; ++j)
    {
      std::cout << coord_mtx[i][j*3] << " " <<
                   coord_mtx[i][j*3+1] << " " <<
                   coord_mtx[i][j*3+2] << std::endl;
    }
    std::cout << param_mtx[i][0] << " " <<
                 param_mtx[i][1] << " " <<
                 param_mtx[i][2] << " " <<
                 param_mtx[i][3] << " " <<
                 param_mtx[i][4] << std::endl;
  }
#endif

  double sum = 1.0e0;
  double coordm[3*COORDIM] = {0.0e0};
  double hc[3*COORDIM][3*COORDIM] = {0.0e0};

  for (int l=0; l<num_of_ent; ++l)
  {
    sum += 1.0e0;
    for (int i=0; i<(3*COORDIM); ++i)
      coordm[i] += (coord_mtx[l][i]-coordm[i])/sum;

    for (int i=0; i<(3*COORDIM); ++i)
    {
      for (int j=0; j<(3*COORDIM); ++j)
      {
        hc[i][j] += ((coord_mtx[l][i] - coordm[i])*
                     (coord_mtx[l][j] - coordm[j])-
                     (sum-1.0e0)*hc[i][j]/sum)/(sum-1.0e0);
      }
    }
  }

  for (int i=0; i<(3*COORDIM); ++i)
    for (int j=i+1; j<(3*COORDIM); ++j)
      if (hc[i][j] != hc[j][i])
        std::cout << i << " " << j << " " <<
          hc[i][j] << " ERROR" << std::endl;;

  arma::mat hca = arma::zeros<arma::mat>(3*COORDIM,3*COORDIM);
  for (int i=0; i<(3*COORDIM); ++i)
    for (int j=0; j<(3*COORDIM); ++j)
      hca(i,j) = hc[i][j];

  arma::vec eigval;
  arma::mat eigvec;

  arma::eig_sym(eigval, eigvec, hca);

  double totval = 0.0e0;
  for (int i=0; i<(3*COORDIM); ++i)
    totval += eigval(i);

  int j = 1;
  double totvar = 0.0e0;
  for (int i=(3*COORDIM-1); i>=0; --i)
  {
    if (j <= 5)
      totvar += 100.0e0*(eigval(i)/totval);
    ++j;

    std::cout << i+1 << " ==> " << 100.0e0*(eigval(i)/totval) << std::endl;
  }
  std::cout << "5 eigenvalues: " << totvar << std::endl;

  arma::mat hcai = arma::zeros<arma::mat>(3*COORDIM,3*COORDIM);
  hcai = hca.i();

  std::cout << hca * hcai ;

  // and so on ...

  // documento Annovi
  // calcolo matrice di correlazione traccie HC
  // diagonalizzo HC e determino A, matrice 5 autovettori principali (5 componenti pricipali)
  // A matrice rotazione che mi permette di calcolare la traslazione usando i paamtri di tracce
  // simulate.

  // documento ATLAS
  // calcolare V e data V calcolare inversa V e quindi C, matrice di rotazione
  // data C determinare il vettore di traslazione q
  // c plus q costanti PCA
  // write constants in a file

  for (int i = 0; i < num_of_ent; ++i)
  {
    delete(coord_mtx[i]);
    delete(param_mtx[i]);
  }
  delete(coord_mtx);
  delete(param_mtx);

  return 0;
}
<|endoftext|>


<commit_before><commit_msg>Planning: minor changes in path_reuse_decider<commit_after><|endoftext|>


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

#include "chrome/browser/sync/util/extensions_activity_monitor.h"

#include "base/file_path.h"
#include "base/string_util.h"
#include "base/waitable_event.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/extensions/extension_bookmarks_module.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/notification_service.h"
#include "testing/gtest/include/gtest/gtest.h"

using browser_sync::ExtensionsActivityMonitor;
namespace keys = extension_manifest_keys;

namespace {

const char* kTestExtensionPath1 = "c:\\testextension1";
const char* kTestExtensionPath2 = "c:\\testextension2";
const char* kTestExtensionVersion = "1.0.0.0";
const char* kTestExtensionName = "foo extension";

template <class FunctionType>
class BookmarkAPIEventTask : public Task {
 public:
  BookmarkAPIEventTask(FunctionType* t, Extension* e, size_t repeats,
                       base::WaitableEvent* done) :
       extension_(e), function_(t), repeats_(repeats), done_(done) {}
   virtual void Run() {
     for (size_t i = 0; i < repeats_; i++) {
       NotificationService::current()->Notify(
           NotificationType::EXTENSION_BOOKMARKS_API_INVOKED,
           Source<Extension>(extension_.get()),
           Details<const BookmarksFunction>(function_.get()));
     }
     done_->Signal();
   }
 private:
  scoped_ptr<Extension> extension_;
  scoped_refptr<FunctionType> function_;
  size_t repeats_;
  base::WaitableEvent* done_;

  DISALLOW_COPY_AND_ASSIGN(BookmarkAPIEventTask);
};

class BookmarkAPIEventGenerator {
 public:
  BookmarkAPIEventGenerator() {}
  virtual ~BookmarkAPIEventGenerator() {}
  template <class T>
  void NewEvent(const std::string& extension_path,
      T* bookmarks_function, size_t repeats) {
#if defined(OS_WIN)
    FilePath path(UTF8ToWide(extension_path));
#elif defined(OS_POSIX)
    FilePath path(extension_path);
#endif
    Extension* extension = new Extension(path);
    std::string error;
    DictionaryValue input;
    input.SetString(keys::kVersion, kTestExtensionVersion);
    input.SetString(keys::kName, kTestExtensionName);
    extension->InitFromValue(input, false, &error);
    bookmarks_function->set_name(T::function_name());
    base::WaitableEvent done_event(false, false);
    ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
        new BookmarkAPIEventTask<T>(bookmarks_function, extension,
                                    repeats, &done_event));
    done_event.Wait();
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(BookmarkAPIEventGenerator);
};
}  // namespace

class DoUIThreadSetupTask : public Task {
 public:
  DoUIThreadSetupTask(NotificationService** service,
                      base::WaitableEvent* done)
      : service_(service), signal_when_done_(done) {}
  virtual ~DoUIThreadSetupTask() {}
  virtual void Run() {
    *service_ = new NotificationService();
    signal_when_done_->Signal();
  }
 private:
  NotificationService** service_;
  base::WaitableEvent* signal_when_done_;
  DISALLOW_COPY_AND_ASSIGN(DoUIThreadSetupTask);
};

class ExtensionsActivityMonitorTest : public testing::Test {
 public:
  ExtensionsActivityMonitorTest() : service_(NULL),
      ui_thread_(ChromeThread::UI) { }
  virtual ~ExtensionsActivityMonitorTest() {}

  virtual void SetUp() {
    ui_thread_.Start();
    base::WaitableEvent service_created(false, false);
    ui_thread_.message_loop()->PostTask(FROM_HERE,
        new DoUIThreadSetupTask(&service_, &service_created));
    service_created.Wait();
  }

  virtual void TearDown() {
    ui_thread_.message_loop()->DeleteSoon(FROM_HERE, service_);
    ui_thread_.Stop();
  }

  MessageLoop* ui_loop() { return ui_thread_.message_loop(); }

  static std::string GetExtensionIdForPath(const std::string& extension_path) {
    std::string error;
#if defined(OS_WIN)
    FilePath path(UTF8ToWide(extension_path));
#elif defined(OS_POSIX)
    FilePath path(extension_path);
#endif
    Extension e(path);
    DictionaryValue input;
    input.SetString(keys::kVersion, kTestExtensionVersion);
    input.SetString(keys::kName, kTestExtensionName);
    e.InitFromValue(input, false, &error);
    EXPECT_EQ("", error);
    return e.id();
  }
 private:
  NotificationService* service_;
  ChromeThread ui_thread_;
};

TEST_F(ExtensionsActivityMonitorTest, Basic) {
  ExtensionsActivityMonitor* monitor = new ExtensionsActivityMonitor();
  BookmarkAPIEventGenerator generator;

  generator.NewEvent<RemoveBookmarkFunction>(kTestExtensionPath1,
      new RemoveBookmarkFunction(), 1);
  generator.NewEvent<MoveBookmarkFunction>(kTestExtensionPath1,
      new MoveBookmarkFunction(), 1);
  generator.NewEvent<UpdateBookmarkFunction>(kTestExtensionPath1,
      new UpdateBookmarkFunction(), 2);
  generator.NewEvent<CreateBookmarkFunction>(kTestExtensionPath1,
      new CreateBookmarkFunction(), 3);
  generator.NewEvent<SearchBookmarksFunction>(kTestExtensionPath1,
      new SearchBookmarksFunction(), 5);
  const uint32 writes_by_extension1 = 1 + 1 + 2 + 3;

  generator.NewEvent<RemoveTreeBookmarkFunction>(kTestExtensionPath2,
      new RemoveTreeBookmarkFunction(), 8);
  generator.NewEvent<GetBookmarkTreeFunction>(kTestExtensionPath2,
      new GetBookmarkTreeFunction(), 13);
  generator.NewEvent<GetBookmarkChildrenFunction>(kTestExtensionPath2,
      new GetBookmarkChildrenFunction(), 21);
  generator.NewEvent<GetBookmarksFunction>(kTestExtensionPath2,
      new GetBookmarksFunction(), 33);
  const uint32 writes_by_extension2 = 8;

  ExtensionsActivityMonitor::Records results;
  monitor->GetAndClearRecords(&results);

  std::string id1 = GetExtensionIdForPath(kTestExtensionPath1);
  std::string id2 = GetExtensionIdForPath(kTestExtensionPath2);

  EXPECT_EQ(2U, results.size());
  EXPECT_TRUE(results.end() != results.find(id1));
  EXPECT_TRUE(results.end() != results.find(id2));
  EXPECT_EQ(writes_by_extension1, results[id1].bookmark_write_count);
  EXPECT_EQ(writes_by_extension2, results[id2].bookmark_write_count);

  ui_loop()->DeleteSoon(FROM_HERE, monitor);
}

TEST_F(ExtensionsActivityMonitorTest, Put) {
  ExtensionsActivityMonitor* monitor = new ExtensionsActivityMonitor();
  BookmarkAPIEventGenerator generator;
  std::string id1 = GetExtensionIdForPath(kTestExtensionPath1);
  std::string id2 = GetExtensionIdForPath(kTestExtensionPath2);

  generator.NewEvent<CreateBookmarkFunction>(kTestExtensionPath1,
      new CreateBookmarkFunction(), 5);
  generator.NewEvent<MoveBookmarkFunction>(kTestExtensionPath2,
      new MoveBookmarkFunction(), 8);

  ExtensionsActivityMonitor::Records results;
  monitor->GetAndClearRecords(&results);

  EXPECT_EQ(2U, results.size());
  EXPECT_EQ(5U, results[id1].bookmark_write_count);
  EXPECT_EQ(8U, results[id2].bookmark_write_count);

  generator.NewEvent<GetBookmarksFunction>(kTestExtensionPath2,
      new GetBookmarksFunction(), 3);
  generator.NewEvent<UpdateBookmarkFunction>(kTestExtensionPath2,
      new UpdateBookmarkFunction(), 2);

  // Simulate a commit failure, which augments the active record set with the
  // refugee records.
  monitor->PutRecords(results);
  ExtensionsActivityMonitor::Records new_records;
  monitor->GetAndClearRecords(&new_records);

  EXPECT_EQ(2U, results.size());
  EXPECT_EQ(id1, new_records[id1].extension_id);
  EXPECT_EQ(id2, new_records[id2].extension_id);
  EXPECT_EQ(5U, new_records[id1].bookmark_write_count);
  EXPECT_EQ(8U + 2U, new_records[id2].bookmark_write_count);
  ui_loop()->DeleteSoon(FROM_HERE, monitor);
}

TEST_F(ExtensionsActivityMonitorTest, MultiGet) {
  ExtensionsActivityMonitor* monitor = new ExtensionsActivityMonitor();
  BookmarkAPIEventGenerator generator;
  std::string id1 = GetExtensionIdForPath(kTestExtensionPath1);

  generator.NewEvent<CreateBookmarkFunction>(kTestExtensionPath1,
      new CreateBookmarkFunction(), 5);

  ExtensionsActivityMonitor::Records results;
  monitor->GetAndClearRecords(&results);

  EXPECT_EQ(1U, results.size());
  EXPECT_EQ(5U, results[id1].bookmark_write_count);

  monitor->GetAndClearRecords(&results);
  EXPECT_TRUE(results.empty());

  generator.NewEvent<CreateBookmarkFunction>(kTestExtensionPath1,
      new CreateBookmarkFunction(), 3);
  monitor->GetAndClearRecords(&results);

  EXPECT_EQ(1U, results.size());
  EXPECT_EQ(3U, results[id1].bookmark_write_count);

  ui_loop()->DeleteSoon(FROM_HERE, monitor);
}
<commit_msg>Fix the extensions activity monitor test for POSIX (c:\?  Really?)<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE entry.

#include "chrome/browser/sync/util/extensions_activity_monitor.h"

#include "base/file_path.h"
#include "base/string_util.h"
#include "base/waitable_event.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/extensions/extension_bookmarks_module.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/notification_service.h"
#include "testing/gtest/include/gtest/gtest.h"

using browser_sync::ExtensionsActivityMonitor;
namespace keys = extension_manifest_keys;

namespace {

const FilePath::CharType kTestExtensionPath1[] =
#if defined(OS_POSIX)
    FILE_PATH_LITERAL("/testextension1");
#elif defined(OS_WIN)
    FILE_PATH_LITERAL("c:\\testextension1");
#endif

const FilePath::CharType kTestExtensionPath2[] =
#if defined(OS_POSIX)
    FILE_PATH_LITERAL("/testextension2");
#elif defined(OS_WIN)
    FILE_PATH_LITERAL("c:\\testextension2");
#endif

const char* kTestExtensionVersion = "1.0.0.0";
const char* kTestExtensionName = "foo extension";

template <class FunctionType>
class BookmarkAPIEventTask : public Task {
 public:
  BookmarkAPIEventTask(FunctionType* t, Extension* e, size_t repeats,
                       base::WaitableEvent* done) :
       extension_(e), function_(t), repeats_(repeats), done_(done) {}
   virtual void Run() {
     for (size_t i = 0; i < repeats_; i++) {
       NotificationService::current()->Notify(
           NotificationType::EXTENSION_BOOKMARKS_API_INVOKED,
           Source<Extension>(extension_.get()),
           Details<const BookmarksFunction>(function_.get()));
     }
     done_->Signal();
   }
 private:
  scoped_ptr<Extension> extension_;
  scoped_refptr<FunctionType> function_;
  size_t repeats_;
  base::WaitableEvent* done_;

  DISALLOW_COPY_AND_ASSIGN(BookmarkAPIEventTask);
};

class BookmarkAPIEventGenerator {
 public:
  BookmarkAPIEventGenerator() {}
  virtual ~BookmarkAPIEventGenerator() {}
  template <class T>
  void NewEvent(const FilePath::StringType& extension_path,
      T* bookmarks_function, size_t repeats) {
    FilePath path(extension_path);
    Extension* extension = new Extension(path);
    std::string error;
    DictionaryValue input;
    input.SetString(keys::kVersion, kTestExtensionVersion);
    input.SetString(keys::kName, kTestExtensionName);
    extension->InitFromValue(input, false, &error);
    bookmarks_function->set_name(T::function_name());
    base::WaitableEvent done_event(false, false);
    ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
        new BookmarkAPIEventTask<T>(bookmarks_function, extension,
                                    repeats, &done_event));
    done_event.Wait();
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(BookmarkAPIEventGenerator);
};
}  // namespace

class DoUIThreadSetupTask : public Task {
 public:
  DoUIThreadSetupTask(NotificationService** service,
                      base::WaitableEvent* done)
      : service_(service), signal_when_done_(done) {}
  virtual ~DoUIThreadSetupTask() {}
  virtual void Run() {
    *service_ = new NotificationService();
    signal_when_done_->Signal();
  }
 private:
  NotificationService** service_;
  base::WaitableEvent* signal_when_done_;
  DISALLOW_COPY_AND_ASSIGN(DoUIThreadSetupTask);
};

class ExtensionsActivityMonitorTest : public testing::Test {
 public:
  ExtensionsActivityMonitorTest() : service_(NULL),
      ui_thread_(ChromeThread::UI) { }
  virtual ~ExtensionsActivityMonitorTest() {}

  virtual void SetUp() {
    ui_thread_.Start();
    base::WaitableEvent service_created(false, false);
    ui_thread_.message_loop()->PostTask(FROM_HERE,
        new DoUIThreadSetupTask(&service_, &service_created));
    service_created.Wait();
  }

  virtual void TearDown() {
    ui_thread_.message_loop()->DeleteSoon(FROM_HERE, service_);
    ui_thread_.Stop();
  }

  MessageLoop* ui_loop() { return ui_thread_.message_loop(); }

  static std::string GetExtensionIdForPath(
      const FilePath::StringType& extension_path) {
    std::string error;
    FilePath path(extension_path);
    Extension e(path);
    DictionaryValue input;
    input.SetString(keys::kVersion, kTestExtensionVersion);
    input.SetString(keys::kName, kTestExtensionName);
    e.InitFromValue(input, false, &error);
    EXPECT_EQ("", error);
    return e.id();
  }
 private:
  NotificationService* service_;
  ChromeThread ui_thread_;
};

TEST_F(ExtensionsActivityMonitorTest, Basic) {
  ExtensionsActivityMonitor* monitor = new ExtensionsActivityMonitor();
  BookmarkAPIEventGenerator generator;

  generator.NewEvent<RemoveBookmarkFunction>(kTestExtensionPath1,
      new RemoveBookmarkFunction(), 1);
  generator.NewEvent<MoveBookmarkFunction>(kTestExtensionPath1,
      new MoveBookmarkFunction(), 1);
  generator.NewEvent<UpdateBookmarkFunction>(kTestExtensionPath1,
      new UpdateBookmarkFunction(), 2);
  generator.NewEvent<CreateBookmarkFunction>(kTestExtensionPath1,
      new CreateBookmarkFunction(), 3);
  generator.NewEvent<SearchBookmarksFunction>(kTestExtensionPath1,
      new SearchBookmarksFunction(), 5);
  const uint32 writes_by_extension1 = 1 + 1 + 2 + 3;

  generator.NewEvent<RemoveTreeBookmarkFunction>(kTestExtensionPath2,
      new RemoveTreeBookmarkFunction(), 8);
  generator.NewEvent<GetBookmarkTreeFunction>(kTestExtensionPath2,
      new GetBookmarkTreeFunction(), 13);
  generator.NewEvent<GetBookmarkChildrenFunction>(kTestExtensionPath2,
      new GetBookmarkChildrenFunction(), 21);
  generator.NewEvent<GetBookmarksFunction>(kTestExtensionPath2,
      new GetBookmarksFunction(), 33);
  const uint32 writes_by_extension2 = 8;

  ExtensionsActivityMonitor::Records results;
  monitor->GetAndClearRecords(&results);

  std::string id1 = GetExtensionIdForPath(kTestExtensionPath1);
  std::string id2 = GetExtensionIdForPath(kTestExtensionPath2);

  EXPECT_EQ(2U, results.size());
  EXPECT_TRUE(results.end() != results.find(id1));
  EXPECT_TRUE(results.end() != results.find(id2));
  EXPECT_EQ(writes_by_extension1, results[id1].bookmark_write_count);
  EXPECT_EQ(writes_by_extension2, results[id2].bookmark_write_count);

  ui_loop()->DeleteSoon(FROM_HERE, monitor);
}

TEST_F(ExtensionsActivityMonitorTest, Put) {
  ExtensionsActivityMonitor* monitor = new ExtensionsActivityMonitor();
  BookmarkAPIEventGenerator generator;
  std::string id1 = GetExtensionIdForPath(kTestExtensionPath1);
  std::string id2 = GetExtensionIdForPath(kTestExtensionPath2);

  generator.NewEvent<CreateBookmarkFunction>(kTestExtensionPath1,
      new CreateBookmarkFunction(), 5);
  generator.NewEvent<MoveBookmarkFunction>(kTestExtensionPath2,
      new MoveBookmarkFunction(), 8);

  ExtensionsActivityMonitor::Records results;
  monitor->GetAndClearRecords(&results);

  EXPECT_EQ(2U, results.size());
  EXPECT_EQ(5U, results[id1].bookmark_write_count);
  EXPECT_EQ(8U, results[id2].bookmark_write_count);

  generator.NewEvent<GetBookmarksFunction>(kTestExtensionPath2,
      new GetBookmarksFunction(), 3);
  generator.NewEvent<UpdateBookmarkFunction>(kTestExtensionPath2,
      new UpdateBookmarkFunction(), 2);

  // Simulate a commit failure, which augments the active record set with the
  // refugee records.
  monitor->PutRecords(results);
  ExtensionsActivityMonitor::Records new_records;
  monitor->GetAndClearRecords(&new_records);

  EXPECT_EQ(2U, results.size());
  EXPECT_EQ(id1, new_records[id1].extension_id);
  EXPECT_EQ(id2, new_records[id2].extension_id);
  EXPECT_EQ(5U, new_records[id1].bookmark_write_count);
  EXPECT_EQ(8U + 2U, new_records[id2].bookmark_write_count);
  ui_loop()->DeleteSoon(FROM_HERE, monitor);
}

TEST_F(ExtensionsActivityMonitorTest, MultiGet) {
  ExtensionsActivityMonitor* monitor = new ExtensionsActivityMonitor();
  BookmarkAPIEventGenerator generator;
  std::string id1 = GetExtensionIdForPath(kTestExtensionPath1);

  generator.NewEvent<CreateBookmarkFunction>(kTestExtensionPath1,
      new CreateBookmarkFunction(), 5);

  ExtensionsActivityMonitor::Records results;
  monitor->GetAndClearRecords(&results);

  EXPECT_EQ(1U, results.size());
  EXPECT_EQ(5U, results[id1].bookmark_write_count);

  monitor->GetAndClearRecords(&results);
  EXPECT_TRUE(results.empty());

  generator.NewEvent<CreateBookmarkFunction>(kTestExtensionPath1,
      new CreateBookmarkFunction(), 3);
  monitor->GetAndClearRecords(&results);

  EXPECT_EQ(1U, results.size());
  EXPECT_EQ(3U, results[id1].bookmark_write_count);

  ui_loop()->DeleteSoon(FROM_HERE, monitor);
}
<|endoftext|>


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

#include "chrome/browser/extensions/browser_action_test_util.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/views/browser_actions_container.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension_action.h"
#include "chrome/common/extensions/extension_resource.h"

class BrowserActionsContainerTest : public ExtensionBrowserTest {
 public:
  BrowserActionsContainerTest() : browser_(NULL) {
  }
  virtual ~BrowserActionsContainerTest() {}

  virtual Browser* CreateBrowser(Profile* profile) {
    browser_ = InProcessBrowserTest::CreateBrowser(profile);
    browser_actions_bar_.reset(new BrowserActionTestUtil(browser_));
    return browser_;
  }

  Browser* browser() { return browser_; }

  BrowserActionTestUtil* browser_actions_bar() {
    return browser_actions_bar_.get();
  }

  // Make sure extension with index |extension_index| has an icon.
  void EnsureExtensionHasIcon(int extension_index) {
    if (!browser_actions_bar_->HasIcon(extension_index)) {
      // The icon is loaded asynchronously and a notification is then sent to
      // observers. So we wait on it.
      browser_actions_bar_->WaitForBrowserActionUpdated(extension_index);
    }
    EXPECT_TRUE(browser_actions_bar()->HasIcon(extension_index));
  }

 private:
  scoped_ptr<BrowserActionTestUtil> browser_actions_bar_;

  Browser* browser_;  // Weak.
};

// Test the basic functionality.
IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, Basic) {
  BrowserActionsContainer::disable_animations_during_testing_ = true;

  // Load an extension with no browser action.
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
                                          .AppendASCII("browser_action")
                                          .AppendASCII("none")));
  // This extension should not be in the model (has no browser action).
  EXPECT_EQ(0, browser_actions_bar()->NumberOfBrowserActions());

  // Load an extension with a browser action.
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
                                          .AppendASCII("browser_action")
                                          .AppendASCII("basics")));
  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
  EnsureExtensionHasIcon(0);

  // Unload the extension.
  std::string id = browser_actions_bar()->GetExtensionId(0);
  UnloadExtension(id);
  EXPECT_EQ(0, browser_actions_bar()->NumberOfBrowserActions());
}

// TODO(mpcomplete): http://code.google.com/p/chromium/issues/detail?id=38992
// Disabled, http://crbug.com/38992.
IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, DISABLED_Visibility) {
  BrowserActionsContainer::disable_animations_during_testing_ = true;

  // Load extension A (contains browser action).
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
                                          .AppendASCII("browser_action")
                                          .AppendASCII("basics")));
  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
  EnsureExtensionHasIcon(0);
  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
  std::string idA = browser_actions_bar()->GetExtensionId(0);

  // Load extension B (contains browser action).
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
                                          .AppendASCII("browser_action")
                                          .AppendASCII("add_popup")));
  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
  EnsureExtensionHasIcon(0);
  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
  std::string idB = browser_actions_bar()->GetExtensionId(1);

  EXPECT_NE(idA, idB);

  // Load extension C (contains browser action).
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
                                          .AppendASCII("browser_action")
                                          .AppendASCII("remove_popup")));
  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
  EnsureExtensionHasIcon(2);
  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
  std::string idC = browser_actions_bar()->GetExtensionId(2);

  // Change container to show only one action, rest in overflow: A, [B, C].
  browser_actions_bar()->SetIconVisibilityCount(1);
  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());

  // Disable extension A (should disappear). State becomes: B [C].
  DisableExtension(idA);
  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
  EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0));

  // Enable A again. A should get its spot in the same location and the bar
  // should not grow (chevron is showing). For details: http://crbug.com/35349.
  // State becomes: A, [B, C].
  EnableExtension(idA);
  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));

  // Disable C (in overflow). State becomes: A, [B].
  DisableExtension(idC);
  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));

  // Enable C again. State becomes: A, [B, C].
  EnableExtension(idC);
  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));

  // Now we have 3 extensions. Make sure they are all visible. State: A, B, C.
  browser_actions_bar()->SetIconVisibilityCount(3);
  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());

  // Disable extension A (should disappear). State becomes: B, C.
  DisableExtension(idA);
  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
  EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0));

  // Disable extension B (should disappear). State becomes: C.
  DisableExtension(idB);
  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
  EXPECT_EQ(idC, browser_actions_bar()->GetExtensionId(0));

  // Enable B (makes B and C showing now). State becomes: B, C.
  EnableExtension(idB);
  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
  EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0));

  // Enable A (makes A, B and C showing now). State becomes: B, C, A.
  EnableExtension(idA);
  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(2));
}

IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, ForceHide) {
  BrowserActionsContainer::disable_animations_during_testing_ = true;

  // Load extension A (contains browser action).
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
                                          .AppendASCII("browser_action")
                                          .AppendASCII("basics")));
  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
  EnsureExtensionHasIcon(0);
  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
  std::string idA = browser_actions_bar()->GetExtensionId(0);

  // Force hide this browser action.
  ExtensionsService* service = browser()->profile()->GetExtensionsService();
  service->SetBrowserActionVisibility(service->GetExtensionById(idA, false),
                                      false);
  EXPECT_EQ(0, browser_actions_bar()->VisibleBrowserActions());

  ReloadExtension(idA);

  // The browser action should become visible again.
  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
}

IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, TestCrash57536) {
  std::cout << "Test starting\n" << std::flush;

  ExtensionsService* service = browser()->profile()->GetExtensionsService();
  const size_t size_before = service->extensions()->size();

  std::cout << "Loading extension\n" << std::flush;

  // Load extension A (contains browser action).
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
                                          .AppendASCII("browser_action")
                                          .AppendASCII("crash_57536")));

  const Extension* extension = service->extensions()->at(size_before);

  std::cout << "Creating bitmap\n" << std::flush;

  // Create and cache and empty bitmap.
  SkBitmap bitmap;
  bitmap.setConfig(SkBitmap::kARGB_8888_Config,
                    Extension::kBrowserActionIconMaxSize,
                    Extension::kBrowserActionIconMaxSize);
  bitmap.allocPixels();

  std::cout << "Set as cached image\n" << std::flush;

  gfx::Size size(Extension::kBrowserActionIconMaxSize,
                 Extension::kBrowserActionIconMaxSize);
  extension->SetCachedImage(
      extension->GetResource(extension->browser_action()->default_icon_path()),
      bitmap,
      size);

  std::cout << "Disabling extension\n" << std::flush;
  DisableExtension(extension->id());
  std::cout << "Enabling extension\n" << std::flush;
  EnableExtension(extension->id());
  std::cout << "Test ending\n" << std::flush;
}
<commit_msg>Reenable BrowserActionsContainerTest.Visibility with timing info, since I suspect the problem is that the test is just a bit too long for the timeout value we use.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/browser_action_test_util.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/views/browser_actions_container.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension_action.h"
#include "chrome/common/extensions/extension_resource.h"

class BrowserActionsContainerTest : public ExtensionBrowserTest {
 public:
  BrowserActionsContainerTest() : browser_(NULL) {
  }
  virtual ~BrowserActionsContainerTest() {}

  virtual Browser* CreateBrowser(Profile* profile) {
    browser_ = InProcessBrowserTest::CreateBrowser(profile);
    browser_actions_bar_.reset(new BrowserActionTestUtil(browser_));
    return browser_;
  }

  Browser* browser() { return browser_; }

  BrowserActionTestUtil* browser_actions_bar() {
    return browser_actions_bar_.get();
  }

  // Make sure extension with index |extension_index| has an icon.
  void EnsureExtensionHasIcon(int extension_index) {
    if (!browser_actions_bar_->HasIcon(extension_index)) {
      // The icon is loaded asynchronously and a notification is then sent to
      // observers. So we wait on it.
      browser_actions_bar_->WaitForBrowserActionUpdated(extension_index);
    }
    EXPECT_TRUE(browser_actions_bar()->HasIcon(extension_index));
  }

 private:
  scoped_ptr<BrowserActionTestUtil> browser_actions_bar_;

  Browser* browser_;  // Weak.
};

// Test the basic functionality.
IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, Basic) {
  BrowserActionsContainer::disable_animations_during_testing_ = true;

  // Load an extension with no browser action.
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
                                          .AppendASCII("browser_action")
                                          .AppendASCII("none")));
  // This extension should not be in the model (has no browser action).
  EXPECT_EQ(0, browser_actions_bar()->NumberOfBrowserActions());

  // Load an extension with a browser action.
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
                                          .AppendASCII("browser_action")
                                          .AppendASCII("basics")));
  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
  EnsureExtensionHasIcon(0);

  // Unload the extension.
  std::string id = browser_actions_bar()->GetExtensionId(0);
  UnloadExtension(id);
  EXPECT_EQ(0, browser_actions_bar()->NumberOfBrowserActions());
}

// TODO(mpcomplete): http://code.google.com/p/chromium/issues/detail?id=38992
IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, Visibility) {
  BrowserActionsContainer::disable_animations_during_testing_ = true;

  base::TimeTicks start_time = base::TimeTicks::Now();

  // Load extension A (contains browser action).
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
                                          .AppendASCII("browser_action")
                                          .AppendASCII("basics")));
  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
  EnsureExtensionHasIcon(0);
  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
  std::string idA = browser_actions_bar()->GetExtensionId(0);

  LOG(INFO) << "Load extension A done  : "
            << (base::TimeTicks::Now() - start_time).InMilliseconds()
            << " ms" << std::flush;

  // Load extension B (contains browser action).
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
                                          .AppendASCII("browser_action")
                                          .AppendASCII("add_popup")));
  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
  EnsureExtensionHasIcon(0);
  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
  std::string idB = browser_actions_bar()->GetExtensionId(1);

  LOG(INFO) << "Load extension B done  : "
            << (base::TimeTicks::Now() - start_time).InMilliseconds()
            << " ms" << std::flush;

  EXPECT_NE(idA, idB);

  // Load extension C (contains browser action).
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
                                          .AppendASCII("browser_action")
                                          .AppendASCII("remove_popup")));
  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
  EnsureExtensionHasIcon(2);
  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
  std::string idC = browser_actions_bar()->GetExtensionId(2);

  LOG(INFO) << "Load extension C done  : "
            << (base::TimeTicks::Now() - start_time).InMilliseconds()
            << " ms" << std::flush;

  // Change container to show only one action, rest in overflow: A, [B, C].
  browser_actions_bar()->SetIconVisibilityCount(1);
  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());

  LOG(INFO) << "Icon visibility count 1: "
            << (base::TimeTicks::Now() - start_time).InMilliseconds()
            << " ms" << std::flush;

  // Disable extension A (should disappear). State becomes: B [C].
  DisableExtension(idA);
  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
  EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0));

  LOG(INFO) << "Disable extension A    : "
            << (base::TimeTicks::Now() - start_time).InMilliseconds()
            << " ms" << std::flush;

  // Enable A again. A should get its spot in the same location and the bar
  // should not grow (chevron is showing). For details: http://crbug.com/35349.
  // State becomes: A, [B, C].
  EnableExtension(idA);
  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));

  LOG(INFO) << "Enable extension A     : "
            << (base::TimeTicks::Now() - start_time).InMilliseconds()
            << " ms" << std::flush;

  // Disable C (in overflow). State becomes: A, [B].
  DisableExtension(idC);
  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));

  LOG(INFO) << "Disable extension C    : "
            << (base::TimeTicks::Now() - start_time).InMilliseconds()
            << " ms" << std::flush;

  // Enable C again. State becomes: A, [B, C].
  EnableExtension(idC);
  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));

  LOG(INFO) << "Enable extension C     : "
            << (base::TimeTicks::Now() - start_time).InMilliseconds()
            << " ms" << std::flush;

  // Now we have 3 extensions. Make sure they are all visible. State: A, B, C.
  browser_actions_bar()->SetIconVisibilityCount(3);
  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());

  LOG(INFO) << "Checkpoint             : "
            << (base::TimeTicks::Now() - start_time).InMilliseconds()
            << " ms" << std::flush;

  // Disable extension A (should disappear). State becomes: B, C.
  DisableExtension(idA);
  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
  EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0));

  LOG(INFO) << "Disable extension A    : "
            << (base::TimeTicks::Now() - start_time).InMilliseconds()
            << " ms" << std::flush;

  // Disable extension B (should disappear). State becomes: C.
  DisableExtension(idB);
  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
  EXPECT_EQ(idC, browser_actions_bar()->GetExtensionId(0));

  LOG(INFO) << "Disable extension B    : "
            << (base::TimeTicks::Now() - start_time).InMilliseconds()
            << " ms" << std::flush;

  // Enable B (makes B and C showing now). State becomes: B, C.
  EnableExtension(idB);
  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
  EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0));

  LOG(INFO) << "Enable extension B     : "
            << (base::TimeTicks::Now() - start_time).InMilliseconds()
            << " ms" << std::flush;

  // Enable A (makes A, B and C showing now). State becomes: B, C, A.
  EnableExtension(idA);
  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(2));

  LOG(INFO) << "Test complete          : "
            << (base::TimeTicks::Now() - start_time).InMilliseconds()
            << " ms" << std::flush;
}

IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, ForceHide) {
  BrowserActionsContainer::disable_animations_during_testing_ = true;

  // Load extension A (contains browser action).
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
                                          .AppendASCII("browser_action")
                                          .AppendASCII("basics")));
  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
  EnsureExtensionHasIcon(0);
  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
  std::string idA = browser_actions_bar()->GetExtensionId(0);

  // Force hide this browser action.
  ExtensionsService* service = browser()->profile()->GetExtensionsService();
  service->SetBrowserActionVisibility(service->GetExtensionById(idA, false),
                                      false);
  EXPECT_EQ(0, browser_actions_bar()->VisibleBrowserActions());

  ReloadExtension(idA);

  // The browser action should become visible again.
  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
}

IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, TestCrash57536) {
  LOG(INFO) << "Test starting\n" << std::flush;

  ExtensionsService* service = browser()->profile()->GetExtensionsService();
  const size_t size_before = service->extensions()->size();

  LOG(INFO) << "Loading extension\n" << std::flush;

  // Load extension A (contains browser action).
  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
                                          .AppendASCII("browser_action")
                                          .AppendASCII("crash_57536")));

  const Extension* extension = service->extensions()->at(size_before);

  LOG(INFO) << "Creating bitmap\n" << std::flush;

  // Create and cache and empty bitmap.
  SkBitmap bitmap;
  bitmap.setConfig(SkBitmap::kARGB_8888_Config,
                    Extension::kBrowserActionIconMaxSize,
                    Extension::kBrowserActionIconMaxSize);
  bitmap.allocPixels();

  LOG(INFO) << "Set as cached image\n" << std::flush;

  gfx::Size size(Extension::kBrowserActionIconMaxSize,
                 Extension::kBrowserActionIconMaxSize);
  extension->SetCachedImage(
      extension->GetResource(extension->browser_action()->default_icon_path()),
      bitmap,
      size);

  LOG(INFO) << "Disabling extension\n" << std::flush;
  DisableExtension(extension->id());
  LOG(INFO) << "Enabling extension\n" << std::flush;
  EnableExtension(extension->id());
  LOG(INFO) << "Test ending\n" << std::flush;
}
<|endoftext|>


<commit_before>//
// "$Id$"
//
// Filename list routines for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2005 by Bill Spitzak and others.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
//
// Please report all bugs and problems on the following page:
//
//     http://www.fltk.org/str.php
//

// Wrapper for scandir with const-correct function prototypes.

#include <FL/filename.H>
#include "flstring.h"
#include <stdlib.h>

// OTB Modifications
//#include <config.h>
#include "fltk-config.h"

extern "C" {
#ifndef HAVE_SCANDIR
  int fl_scandir (const char *dir, dirent ***namelist,
	          int (*select)(dirent *),
	          int (*compar)(dirent **, dirent **));
#  define scandir	fl_scandir
#endif
}

int fl_alphasort(struct dirent **a, struct dirent **b) {
  return strcmp((*a)->d_name, (*b)->d_name);
}

int fl_casealphasort(struct dirent **a, struct dirent **b) {
  return strcasecmp((*a)->d_name, (*b)->d_name);
}

int fl_filename_list(const char *d, dirent ***list,
                     Fl_File_Sort_F *sort) {
#ifndef HAVE_SCANDIR
  int n = scandir(d, list, 0, sort);
#elif defined(__hpux) || defined(__CYGWIN__)
  // HP-UX, Cygwin define the comparison function like this:
  int n = scandir(d, list, 0, (int(*)(const dirent **, const dirent **))sort);
#elif defined(__osf__)
  // OSF, DU 4.0x
  int n = scandir(d, list, 0, (int(*)(dirent **, dirent **))sort);
#elif defined(_AIX)
  // AIX is almost standard...
  int n = scandir(d, list, 0, (int(*)(void*, void*))sort);
#elif !defined(__sgi)
  // The vast majority of UNIX systems want the sort function to have this
  // prototype, most likely so that it can be passed to qsort without any
  // changes:
  int n = scandir(d, list, 0, (int(*)(const void*,const void*))sort);
#else
  // This version is when we define our own scandir (WIN32 and perhaps
  // some Unix systems) and apparently on IRIX:
  int n = scandir(d, list, 0, sort);
#endif

#if defined(WIN32) && !defined(__CYGWIN__)
  // we did this already during fl_scandir/win32
#else
  // append a '/' to all filenames that are directories
  int i, dirlen = strlen(d);
  char *fullname = (char*)malloc(dirlen+FL_PATH_MAX+3); // Add enough extra for two /'s and a nul
  // Use memcpy for speed since we already know the length of the string...
  memcpy(fullname, d, dirlen+1);
  char *name = fullname + dirlen;
  if (name!=fullname && name[-1]!='/') *name++ = '/';
  for (i=0; i<n; i++) {
    dirent *de = (*list)[i];
    int len = strlen(de->d_name);
    if (de->d_name[len-1]=='/' || len>FL_PATH_MAX) continue;
    // Use memcpy for speed since we already know the length of the string...
    memcpy(name, de->d_name, len+1);
    if (fl_filename_isdir(fullname)) {
      (*list)[i] = de = (dirent*)realloc(de, de->d_name - (char*)de + len + 2);
      char *dst = de->d_name + len;
      *dst++ = '/';
      *dst = 0;
    }
  }
  free(fullname);
#endif
  return n;
}

//
// End of "$Id$".
//
<commit_msg>COMP: fix FLTK compilation problem on open solaris<commit_after>//
// "$Id$"
//
// Filename list routines for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2005 by Bill Spitzak and others.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
//
// Please report all bugs and problems on the following page:
//
//     http://www.fltk.org/str.php
//

// Wrapper for scandir with const-correct function prototypes.

#include <FL/filename.H>
#include "flstring.h"
#include <stdlib.h>

// OTB Modifications
//#include <config.h>
#include "fltk-config.h"

extern "C" {
#ifndef HAVE_SCANDIR
  int fl_scandir (const char *dir, dirent ***namelist,
	          int (*select)(dirent *),
	          int (*compar)(dirent **, dirent **));
#  define scandir	fl_scandir
#endif
}

int fl_alphasort(struct dirent **a, struct dirent **b) {
  return strcmp((*a)->d_name, (*b)->d_name);
}

int fl_casealphasort(struct dirent **a, struct dirent **b) {
  return strcasecmp((*a)->d_name, (*b)->d_name);
}

int fl_filename_list(const char *d, dirent ***list,
                     Fl_File_Sort_F *sort) {
#ifndef HAVE_SCANDIR
  int n = scandir(d, list, 0, sort);
#elif defined(__hpux) || defined(__CYGWIN__) || defined(sun)
  // HP-UX, Cygwin define the comparison function like this:
  int n = scandir(d, list, 0, (int(*)(const dirent **, const dirent **))sort);
#elif defined(__osf__)
  // OSF, DU 4.0x
  int n = scandir(d, list, 0, (int(*)(dirent **, dirent **))sort);
#elif defined(_AIX)
  // AIX is almost standard...
  int n = scandir(d, list, 0, (int(*)(void*, void*))sort);
#elif !defined(__sgi)
  // The vast majority of UNIX systems want the sort function to have this
  // prototype, most likely so that it can be passed to qsort without any
  // changes:
  int n = scandir(d, list, 0, (int(*)(const void*,const void*))sort);
#else
  // This version is when we define our own scandir (WIN32 and perhaps
  // some Unix systems) and apparently on IRIX:
  int n = scandir(d, list, 0, sort);
#endif

#if defined(WIN32) && !defined(__CYGWIN__)
  // we did this already during fl_scandir/win32
#else
  // append a '/' to all filenames that are directories
  int i, dirlen = strlen(d);
  char *fullname = (char*)malloc(dirlen+FL_PATH_MAX+3); // Add enough extra for two /'s and a nul
  // Use memcpy for speed since we already know the length of the string...
  memcpy(fullname, d, dirlen+1);
  char *name = fullname + dirlen;
  if (name!=fullname && name[-1]!='/') *name++ = '/';
  for (i=0; i<n; i++) {
    dirent *de = (*list)[i];
    int len = strlen(de->d_name);
    if (de->d_name[len-1]=='/' || len>FL_PATH_MAX) continue;
    // Use memcpy for speed since we already know the length of the string...
    memcpy(name, de->d_name, len+1);
    if (fl_filename_isdir(fullname)) {
      (*list)[i] = de = (dirent*)realloc(de, de->d_name - (char*)de + len + 2);
      char *dst = de->d_name + len;
      *dst++ = '/';
      *dst = 0;
    }
  }
  free(fullname);
#endif
  return n;
}

//
// End of "$Id$".
//
<|endoftext|>