text:
#include <reaplus/ReaPlusIntegrationTest.h>
#include <reaplus/Reaper.h>
#include <reaplus/Project.h>
#include <reaplus/Track.h>
#include <reaplus/TrackSend.h>
#include <reaplus/Section.h>
#include <reaplus/Action.h>
#include <reaplus/FxChain.h>
#include <reaplus/Fx.h>
#include <reaplus/FxParameter.h>
#include <reaplus/MidiInputDevice.h>
#include <reaplus/IncomingMidiEvent.h>
#include <reaplus/MidiOutputDevice.h>
#include <cstring>
#include <reaper_plugin_functions.h>
#include <boost/range/counting_range.hpp>
#include <boost/range/adaptor/transformed.hpp>

using boost::none;
using boost::optional;
using std::string;
using rxcpp::composite_subscription;
using rxcpp::observable;
using std::function;
using boost::make_counting_iterator;
using boost::make_transform_iterator;
using boost::counting_range;
using boost::adaptors::transformed;
using helgoboss::MidiMessage;
using helgoboss::MidiMessageType;

namespace reaplus {
  void ReaPlusIntegrationTest::tests() {
    // TODO Test tracksReordered event (how to test that?)
    // TODO Test fxClosed event (how to test that?)
    // TODO Test fxFocused with none event (how to test that?)

    // DONE-rust
    test("Boost ranges", [] {
      const auto op = [](int i) { return std::to_string(i); };
      const auto range = counting_range(0, 20) | transformed(op);
      for (const auto& bla : range) {
//        Reaper::instance().showConsoleMessage(bla);
      }
    });

    // DONE-rust
    testWithUntil("Create empty project in new tab", [](auto testIsOver) {
      // Given
      const auto currentProjectBefore = Reaper::instance().currentProject();
      const int projectCountBefore = Reaper::instance().projectCount();

      // When
      optional<Project> eventProject;
      int count = 0;
      Reaper::instance().projectSwitched().take_until(testIsOver).subscribe([&](Project p) {
        count++;
        eventProject = p;
      });
      const auto newProject = Reaper::instance().createEmptyProjectInNewTab();

      // Then
      assertTrue(currentProjectBefore == currentProjectBefore, "Project comparison broken"); // NOLINT
      assertTrue(Reaper::instance().projectCount() == projectCountBefore + 1, "Project count not increased");
      assertTrue(Reaper::instance().projects().as_blocking().count() == projectCountBefore + 1);
      assertTrue(Reaper::instance().currentProject() != currentProjectBefore, "Current project still the same");
      assertTrue(Reaper::instance().currentProject() == newProject, "Current project not new project");
      assertTrue(Reaper::instance().projects().as_blocking().first() != newProject);
      assertTrue(Reaper::instance().projectsWithCurrentOneFirst().as_blocking().first() == newProject);
      assertTrue(Reaper::instance().projectsWithCurrentOneFirst().as_blocking().count() == projectCountBefore + 1);
      assertTrue(newProject.trackCount() == 0, "Project not empty at beginning");
      assertTrue(newProject.index() > 0);
      assertTrue(!newProject.filePath().is_initialized());
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventProject == newProject, "Project event wrong");
    });

    // DONE-rust
    testWithUntil("Add track", [](auto testIsOver) {
      // Given
      auto project = Reaper::instance().currentProject();

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackAdded().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      const auto newTrack = project.addTrack();

      // Then
      assertTrue(project.trackCount() == 1, "Track count not increased");
      assertTrue(newTrack.index() == 0, "Track index wrong");
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == newTrack, "Track event wrong");
    });

    // DONE-rust
    test("Query master track", [] {
      // Given
      auto project = Reaper::instance().currentProject();

      // When
      const auto masterTrack = project.masterTrack();

      // Then
      assertTrue(masterTrack.isMasterTrack(), "Track is not master track");
    });

    // DONE-rust
    test("Query all tracks", [] {
      // Given
      auto project = Reaper::instance().currentProject();
      project.addTrack();

      // When
      auto tracks = project.tracks();

      // Then
      assertTrue(tracks.as_blocking().count() == 2, "Not enough tracks returned");
    });

    // DONE-rust
    test("Query track by GUID", [] {
      // Given
      auto project = Reaper::instance().currentProject();
      auto firstTrack = *project.firstTrack();
      auto newTrack = project.addTrack();

      // When
      auto foundTrack = project.trackByGuid(newTrack.guid());

      // Then
      assertTrue(foundTrack.isAvailable(), "Reported existing track as non-available");
      assertTrue(foundTrack == newTrack, "Didn't find right track");
      assertTrue(foundTrack != firstTrack, "Found wrong track");
      assertTrue(newTrack.guid() == Track::getMediaTrackGuid(newTrack.mediaTrack()),
          "getMediaTrackGuid() doesn't work");
    });

    // DONE-rust
    test("Query non-existent track by GUID", [] {
      // Given
      auto project = Reaper::instance().currentProject();

      // When
      auto foundTrack = project.trackByGuid("noingwrg9898z3h");

      // Then
      assertTrue(!foundTrack.isAvailable(), "Reported non-existent track as available");
    });

    // DONE-rust
    test("Query track project", [] {
      // Given
      auto project = Reaper::instance().currentProject();

      // When
      auto trackProject = project.firstTrack()->project();

      // Then
      assertTrue(trackProject == project, "Track project is not original project");
    });

    // DONE-rust
    test("Query track name", [] {
      // Given
      auto track = firstTrack();

      // When
      auto trackName = track.name();

      // Then
      assertTrue(trackName.empty(), "Wrong name reported");
    });

    // DONE-rust
    testWithUntil("Set track name", [](auto testIsOver) {
      // Given
      auto track = firstTrack();

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackNameChanged().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      track.setName("Foo Bla");

      // Then
      assertTrue(track.name() == "Foo Bla", "Name not correctly set");
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == track, "Track event wrong");
    });

    // DONE-rust
    test("Query track input monitoring", [] {
      // Given
      auto track = firstTrack();

      // When
      auto mode = track.inputMonitoringMode();

      // Then
      assertTrue(mode == InputMonitoringMode::Normal, "Wrong input monitoring mode");
    });

    // DONE-rust
    testWithUntil("Set track input monitoring", [](auto testIsOver) {
      // Given
      auto track = firstTrack();

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackInputMonitoringChanged().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      track.setInputMonitoringMode(InputMonitoringMode::NotWhenPlaying);

      // Then
      assertTrue(track.inputMonitoringMode() == InputMonitoringMode::NotWhenPlaying, "Wrong input monitoring mode");
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == track, "Track event wrong");
    });

    // DONE-rust
    test("Query track recording input", [] {
      // Given
      auto track = firstTrack();

      // When
      auto recInput = track.recordingInput();

      // Then
      assertTrue(recInput != nullptr, "Returned empty recording input");
      assertTrue(recInput->type() == RecordingInputType::Midi, "Returned wrong recording input type");
      auto& midiRecInput = dynamic_cast<MidiRecordingInput&>(*recInput);
      assertTrue(midiRecInput.channel() == none, "Returned explicit channel");
      assertTrue(midiRecInput.device() == none, "Returned explicit device");
      assertTrue(*RecordingInput::ofRecInputIndex(recInput->recInputIndex()) == *recInput, "== doesn't work");
    });

    // DONE-rust
    testWithUntil("Set track recording input 1", [](auto testIsOver) {
      // Given
      auto track = firstTrack();

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackInputChanged().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      track.setRecordingInput(MidiRecordingInput::fromDeviceAndChannel(4, 5));

      // Then
      auto recInput = track.recordingInput();
      assertTrue(recInput != nullptr, "Returned empty recording input");
      assertTrue(recInput->type() == RecordingInputType::Midi, "Returned wrong recording input type");
      auto& midiRecInput = dynamic_cast<MidiRecordingInput&>(*recInput);
      assertTrue(midiRecInput.channel() == 5, "Returned wrong channel");
      assertTrue(midiRecInput.device()->id() == 4, "Returned wrong device");
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == track, "Track event wrong");
    });

    // DONE-rust
    test("Set track recording input 2", [] {
      // Given
      auto track = firstTrack();

      // When
      track.setRecordingInput(MidiRecordingInput::fromAllChannelsOfDevice(7));

      // Then
      auto recInput = track.recordingInput();
      assertTrue(recInput != nullptr, "Returned empty recording input");
      assertTrue(recInput->type() == RecordingInputType::Midi, "Returned wrong recording input type");
      auto& midiRecInput = dynamic_cast<MidiRecordingInput&>(*recInput);
      assertTrue(midiRecInput.channel() == none, "Returned explicit channel");
      assertTrue(midiRecInput.device()->id() == 7, "Returned wrong device");
    });

    // DONE-rust
    test("Set track recording input 3", [] {
      // Given
      auto track = firstTrack();

      // When
      track.setRecordingInput(MidiRecordingInput::fromAllDevicesAndChannels());

      // Then
      auto recInput = track.recordingInput();
      assertTrue(recInput != nullptr, "Returned empty recording input");
      assertTrue(recInput->type() == RecordingInputType::Midi, "Returned wrong recording input type");
      auto& midiRecInput = dynamic_cast<MidiRecordingInput&>(*recInput);
      assertTrue(midiRecInput.channel() == none, "Returned explicit channel");
      assertTrue(midiRecInput.device() == none, "Returned explicit device");
    });

    // DONE-rust
    test("Set track recording input 4", [] {
      // Given
      auto track = firstTrack();

      // When
      track.setRecordingInput(MidiRecordingInput::fromAllDevicesWithChannel(15));

      // Then
      auto recInput = track.recordingInput();
      assertTrue(recInput != nullptr, "Returned empty recording input");
      assertTrue(recInput->type() == RecordingInputType::Midi, "Returned wrong recording input type");
      auto& midiRecInput = dynamic_cast<MidiRecordingInput&>(*recInput);
      assertTrue(midiRecInput.channel() == 15, "Returned wrong channel");
      assertTrue(midiRecInput.device() == none, "Returned explicit device");
    });

    // DONE-rust
    test("Query track volume", [] {
      // Given
      auto track = firstTrack();

      // When
      auto volume = track.volume();

      // Then
      assertTrue(volume.reaperValue() == 1.0, "Wrong REAPER value returned");
      assertTrue(volume.db() == 0.0, "Wrong db returned");
      assertTrue(volume.normalizedValue() == 0.71599999999999997, "Wrong normalized value returned");
    });

    // DONE-rust
    testWithUntil("Set track volume", [](auto testIsOver) {
      // Given
      auto track = firstTrack();

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackVolumeChanged().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      track.setVolume(0.25);

      // Then
      auto volume = track.volume();
      assertTrue(volume.reaperValue() == 0.031588093366685013, "Wrong REAPER value returned");
      assertTrue(volume.db() == -30.009531739774296, "Wrong db returned");
      assertTrue(volume.normalizedValue() == 0.25000000000003497, "Wrong normalized value returned");
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == track, "Track event wrong");
    });

    // DONE-rust
    test("Query track pan", [] {
      // Given
      auto track = firstTrack();

      // When
      auto pan = track.pan();

      // Then
      assertTrue(pan.reaperValue() == 0.0, "Wrong REAPER value returned");
      assertTrue(pan.normalizedValue() == 0.5, "Wrong normalized value returned");
    });

    // DONE-rust
    testWithUntil("Set track pan", [](auto testIsOver) {
      // Given
      auto track = firstTrack();

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackPanChanged().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      track.setPan(0.25);

      // Then
      auto pan = track.pan();
      assertTrue(pan.reaperValue() == -0.5, "Wrong REAPER value returned");
      assertTrue(pan.normalizedValue() == 0.25, "Wrong normalized value returned");
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == track, "Track event wrong");
    });

    // DONE-rust
    test("Query track selection state", [] {
      // Given
      auto project = Reaper::instance().currentProject();
      auto track = firstTrack();

      // When
      bool isSelected = track.isSelected();

      // Then
      assertTrue(!isSelected, "Wrong value returned");
      assertTrue(project.selectedTrackCount() == 0);
    });

    // DONE-rust
    testWithUntil("Select track", [](auto testIsOver) {
      // Given
      auto project = Reaper::instance().currentProject();
      auto track = firstTrack();
      auto track2 = *project.trackByIndex(2);

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackSelectedChanged().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      track.select();
      track2.select();

      // Then
      assertTrue(track.isSelected(), "Track was not selected");
      assertTrue(project.selectedTrackCount() == 2);
      assertTrue(project.firstSelectedTrack().is_initialized());
      assertTrue(project.firstSelectedTrack()->index() == 0);
      assertTrue(project.selectedTracks().as_blocking().count() == 2);
      assertTrue(count == 2, "Event count wrong");
      assertTrue(*eventTrack == track2, "Track event wrong");
    });

    // DONE-rust
    testWithUntil("Unselect track", [](auto testIsOver) {
      // Given
      auto project = Reaper::instance().currentProject();
      auto track = firstTrack();

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackSelectedChanged().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      track.unselect();

      // Then
      assertTrue(!track.isSelected(), "Track was not unselected");
      assertTrue(project.selectedTrackCount() == 1);
      assertTrue(project.firstSelectedTrack().is_initialized());
      assertTrue(project.firstSelectedTrack()->index() == 2);
      assertTrue(project.selectedTracks().as_blocking().count() == 1);
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == track, "Track event wrong");
    });

    // DONE-rust
    testWithUntil("Select master track", [](auto testIsOver) {
      // Given
      auto project = Reaper::instance().currentProject();
      auto masterTrack = project.masterTrack();

      // When
      project.unselectAllTracks();
      project.masterTrack().select();

      // Then
      assertTrue(masterTrack.isSelected(), "Master track was not selected");
      assertTrue(project.selectedTrackCount(true) == 1);
      assertTrue(project.firstSelectedTrack(true).is_initialized());
      assertTrue(project.firstSelectedTrack(true)->isMasterTrack());
      assertTrue(project.selectedTracks(true).as_blocking().count() == 1);
    });

    // DONE-rust
    test("Query track auto arm mode", [] {
      // Given
      auto track = firstTrack();

      // When
      bool isInAutoArmMode = track.hasAutoArmEnabled();

      // Then
      assertTrue(!isInAutoArmMode, "Wrong value returned");
    });

    // DONE-rust
    test("Query track arm state", [] {
      // Given
      auto track = firstTrack();

      // When
      bool isArmed = track.isArmed();
      bool isArmedIgnoringAutoArm = track.isArmed(false);

      // Then
      assertTrue(!isArmed, "Wrong value returned");
      assertTrue(!isArmedIgnoringAutoArm, "Wrong value returned (ignoring auto-arm)");
    });

    // DONE-rust
    testWithUntil("Arm track in normal mode", [](auto testIsOver) {
      // Given
      auto track = firstTrack();

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackArmChanged().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      track.arm();

      // Then
      assertTrue(track.isArmed(), "Track was not armed");
      assertTrue(track.isArmed(false), "Track was not armed (ignoring auto-arm)");
      assertTrue(!track.hasAutoArmEnabled(), "Track is not in normal mode anymore");
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == track, "Track event wrong");
    });

    // DONE-rust
    testWithUntil("Disarm track in normal mode", [](auto testIsOver) {
      // Given
      auto track = firstTrack();

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackArmChanged().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      track.disarm();

      // Then
      assertTrue(!track.isArmed(), "Track was not disarmed");
      assertTrue(!track.isArmed(false), "Track was not disarmed (ignoring auto-arm)");
      assertTrue(!track.hasAutoArmEnabled(), "Track is not in normal mode anymore");
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == track, "Track event wrong");
    });

    // DONE-rust
    test("Enable track auto-arm mode", [] {
      // Given
      auto track = firstTrack();

      // When
      track.enableAutoArm();

      // Then
      assertTrue(track.hasAutoArmEnabled(), "Track is still in normal mode");
      assertTrue(!track.isArmed(), "Track is suddenly armed");
      assertTrue(!track.isArmed(false), "Track is suddenly armed (ignoring auto-arm)");
    });

    // DONE-rust
    testWithUntil("Arm track in auto-arm mode", [](auto testIsOver) {
      // Given
      auto track = firstTrack();

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackArmChanged().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      track.arm();

      // Then
      assertTrue(track.isArmed(), "Track was not armed");
      // TODO Interesting! GetMediaTrackInfo_Value read with I_RECARM seems to support auto-arm already!
      // So maybe we should remove the chunk check and the parameter supportAutoArm
      assertTrue(track.isArmed(false), "Track was not armed (ignoring auto-arm)");
      assertTrue(track.hasAutoArmEnabled(), "Track is not in auto-arm mode anymore");
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == track, "Track event wrong");
    });

    // DONE-rust
    testWithUntil("Disarm track in auto-arm mode", [](auto testIsOver) {
      // Given
      auto track = firstTrack();

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackArmChanged().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      track.disarm();

      // Then
      assertTrue(!track.isArmed(), "Track was not disarmed");
      assertTrue(!track.isArmed(false), "Track was not disarmed (ignoring auto-arm)");
      assertTrue(track.hasAutoArmEnabled(), "Track is not in auto-arm mode anymore");
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == track, "Track event wrong");
    });

    // DONE-rust
    test("Disable track auto-arm mode", [] {
      // Given
      auto track = firstTrack();

      // When
      track.disableAutoArm();

      // Then
      assertTrue(!track.hasAutoArmEnabled(), "Track is still in auto-arm mode");
      assertTrue(!track.isArmed(), "Track is suddenly armed");
      assertTrue(!track.isArmed(false), "Track is suddenly armed (ignoring auto-arm)");
    });

    // DONE-rust
    test("Switch to normal track mode while armed", [] {
      // Given
      auto track = firstTrack();
      track.arm();
      assertTrue(track.isArmed(), "Precondition failed");

      // When
      track.disableAutoArm();

      // Then
      assertTrue(!track.hasAutoArmEnabled(), "Track is still in auto-arm mode");
      assertTrue(track.isArmed(), "Track is suddenly unarmed");
      assertTrue(track.isArmed(false), "Track is suddenly unarmed (ignoring auto-arm)");
    });

    // DONE-rust
    test("Switch track to auto-arm mode while armed", [] {
      // Given
      auto track = firstTrack();
      track.unselect();

      // When
      track.enableAutoArm();

      // Then
      assertTrue(track.hasAutoArmEnabled(), "Track is still in normal mode");
      assertTrue(track.isArmed(), "Track is suddenly unarmed");
      assertTrue(track.isArmed(false), "Track is suddenly unarmed (ignoring auto-arm)");
    });

    // DONE-rust
    testWithUntil("Disarm track in auto-arm mode (ignoring auto-arm)", [](auto testIsOver) {
      // Given
      auto track = firstTrack();

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackArmChanged().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      track.disarm(false);

      // Then
      assertTrue(!track.isArmed(), "Track was not disarmed");
      assertTrue(!track.isArmed(false), "Track was not disarmed (ignoring auto-arm)");
      assertTrue(!track.hasAutoArmEnabled(), "Track is still in auto-arm mode");
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == track, "Track event wrong");
    });

    // DONE-rust
    testWithUntil("Arm track in auto-arm mode (ignoring auto-arm)", [](auto testIsOver) {
      // Given
      auto track = firstTrack();
      track.enableAutoArm();
      assertTrue(track.hasAutoArmEnabled(), "Precondition failed");
      assertTrue(!track.isArmed(), "Precondition failed");

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackArmChanged().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      track.arm(false);

      // Then
      assertTrue(track.isArmed(), "Track was not armed");
      assertTrue(track.isArmed(false), "Track was not armed (ignoring auto-arm)");
      assertTrue(!track.hasAutoArmEnabled(), "Track is still in auto-arm mode");
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == track, "Track event wrong");
    });

    // DONE-rust
    testWithUntil("Select track exclusively", [](auto testIsOver) {
      // Given
      auto project = Reaper::instance().currentProject();
      auto firstTrack = *project.trackByIndex(0);
      auto secondTrack = *project.trackByIndex(1);
      auto thirdTrack = *project.trackByIndex(2);
      firstTrack.unselect();
      secondTrack.select();
      thirdTrack.select();

      // When
      int count = 0;
      Reaper::instance().trackSelectedChanged().take_until(testIsOver).subscribe([&](Track t) {
        count++;
      });
      firstTrack.selectExclusively();

      // Then
      assertTrue(firstTrack.isSelected(), "First track has not been selected");
      assertTrue(!secondTrack.isSelected(), "Second track is still selected");
      assertTrue(!thirdTrack.isSelected(), "Third track is still selected");
      assertTrue(project.selectedTrackCount() == 1);
      assertTrue(project.firstSelectedTrack().is_initialized());
      assertTrue(project.selectedTracks().as_blocking().count() == 1);
      assertTrue(count == 3, "Event count wrong");
    });

    // DONE-rust
    testWithUntil("Remove track", [](auto testIsOver) {
      // Given
      auto project = Reaper::instance().currentProject();
      int trackCountBefore = project.trackCount();
      auto firstTrack = *project.trackByIndex(0);
      auto secondTrack = *project.trackByNumber(2);
      auto secondTrackGuid = secondTrack.guid();
      assertTrue(firstTrack.isAvailable(), "Precondition failed");
      assertTrue(secondTrack.index() == 1, "Precondition failed");
      assertTrue(secondTrack.isAvailable(), "Precondition failed");

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackRemoved().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      project.removeTrack(firstTrack);

      // Then
      assertTrue(project.trackCount() == trackCountBefore - 1, "Track count still same as before");
      assertTrue(!firstTrack.isAvailable(), "Removed track still available");
      assertTrue(secondTrack.index() == 0, "Index of track after removed track not invalidated");
      assertTrue(secondTrack.guid() == secondTrackGuid, "GUID of track after removed track has changed");
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == firstTrack, "Track event wrong");
    });

    // DONE-rust
    test("Query track automation mode", [] {
      // Given
      auto track = firstTrack();

      // When
      auto automationMode = track.automationMode();
      auto globalAutomationOverride = Reaper::instance().globalAutomationOverride();
      auto effectiveAutomationMode = track.effectiveAutomationMode();

      // Then
      assertTrue(automationMode == AutomationMode::TrimRead, "Wrong track automation mode");
      if (globalAutomationOverride == AutomationMode::NoOverride) {
        assertTrue(effectiveAutomationMode == AutomationMode::TrimRead, "Wrong effective track automation mode");
      } else {
        assertTrue(effectiveAutomationMode == globalAutomationOverride, "Wrong effective track automation mode");
      }
    });

    // DONE-rust
    test("Query track send count", [] {
      // Given
      auto track = firstTrack();

      // When
      auto sendCount = track.sendCount();

      // Then
      assertTrue(sendCount == 0, "Wrong send count");
      assertTrue(track.sendByIndex(0) == none, "Send by index returned something");
      assertTrue(!track.sendByTargetTrack(track).isAvailable(), "Send by target returned something");
      assertTrue(!track.indexBasedSendByIndex(0).isAvailable(), "Index-based send by index returned something");
      assertTrue(track.sends().as_blocking().count() == 0, "Sends not empty");
    });

    // DONE-rust
    test("Add track send", [] {
      // Given
      auto project = Reaper::instance().currentProject();
      auto firstTrack = *project.trackByIndex(0);
      auto secondTrack = *project.trackByIndex(1);

      // When
      auto send = firstTrack.addSendTo(secondTrack);

      // Then
      assertTrue(firstTrack.sendCount() == 1, "No send has been added");
      assertTrue(firstTrack.sendByIndex(0).is_initialized(), "Send by index returned nothing");
      assertTrue(*firstTrack.sendByIndex(0) == send, "Send by index returned wrong send");
      assertTrue(firstTrack.sendByTargetTrack(secondTrack).isAvailable(), "Send by target returned noting");
      assertTrue(!secondTrack.sendByTargetTrack(firstTrack).isAvailable(), "Reverse send by target returned something");
      assertTrue(firstTrack.indexBasedSendByIndex(0).isAvailable(), "Index-based send by index returned nothing");
      assertTrue(firstTrack.sends().as_blocking().count() == 1, "Sends returns wrong number");
    });

    // DONE-rust
    test("Query track send", [] {
      // Given
      auto project = Reaper::instance().currentProject();
      auto firstTrack = *project.trackByIndex(0);
      auto secondTrack = *project.trackByIndex(1);
      auto thirdTrack = project.addTrack();

      // When
      auto sendToSecondTrack = firstTrack.sendByTargetTrack(secondTrack);
      auto sendToThirdTrack = firstTrack.addSendTo(thirdTrack);

      // Then
      assertTrue(sendToSecondTrack.isAvailable());
      assertTrue(sendToThirdTrack.isAvailable());
      assertTrue(sendToSecondTrack.index() == 0);
      assertTrue(sendToThirdTrack.index() == 1);
      // assertTrue(sendToSecondTrack.name());
      assertTrue(sendToSecondTrack.sourceTrack() == firstTrack);
      assertTrue(sendToThirdTrack.sourceTrack() == firstTrack);
      assertTrue(sendToSecondTrack.targetTrack() == secondTrack);
      assertTrue(sendToThirdTrack.targetTrack() == thirdTrack);
      assertTrue(sendToSecondTrack.volume().db() == 0);
      assertTrue(sendToThirdTrack.volume().db() == 0);
    });

    // DONE-rust
    testWithUntil("Set track send volume", [](auto testIsOver) {
      // Given
      auto project = Reaper::instance().currentProject();
      auto firstTrack = *project.trackByIndex(0);
      auto thirdTrack = *project.trackByIndex(2);
      auto send = firstTrack.sendByTargetTrack(thirdTrack);

      // When
      optional<TrackSend> eventTrackSend;
      int count = 0;
      Reaper::instance().trackSendVolumeChanged().take_until(testIsOver).subscribe([&](TrackSend s) {
        count++;
        eventTrackSend = s;
      });
      send.setVolume(0.25);

      // Then
      assertTrue(send.volume().db() == -30.009531739774296);
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrackSend == send, "Track send event wrong");
    });

    // DONE-rust
    testWithUntil("Set track send pan", [](auto testIsOver) {
      // Given
      auto project = Reaper::instance().currentProject();
      auto firstTrack = *project.trackByIndex(0);
      auto thirdTrack = *project.trackByIndex(2);
      auto send = firstTrack.sendByTargetTrack(thirdTrack);

      // When
      optional<TrackSend> eventTrackSend;
      int count = 0;
      Reaper::instance().trackSendPanChanged().take_until(testIsOver).subscribe([&](TrackSend s) {
        count++;
        eventTrackSend = s;
      });
      send.setPan(0.25);

      // Then
      assertTrue(send.pan().reaperValue() == -0.5, "Wrong REAPER value returned");
      assertTrue(send.pan().normalizedValue() == 0.25, "Wrong normalized value returned");
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrackSend == send, "Track send event wrong");
    });

    // DONE-rust
    test("Query action", [] {
      // Given
      firstTrack().selectExclusively();
      assertTrue(!firstTrack().isMuted());

      // When
      auto toggleAction = Reaper::instance().mainSection().actionByCommandId(6);
      auto normalAction = Reaper::instance().mainSection().actionByCommandId(41075);
      auto normalActionByIndex = Reaper::instance().mainSection().actionByIndex(normalAction.index());

      // Then
      assertTrue(toggleAction.isAvailable());
      assertTrue(normalAction.isAvailable());
      assertTrue(toggleAction.character() == ActionCharacter::Toggle);
      assertTrue(normalAction.character() == ActionCharacter::Trigger);
      assertTrue(!toggleAction.isOn());
      assertTrue(!normalAction.isOn());
      assertTrue(toggleAction.parameterType() == ParameterType::Action);
      assertTrue(*toggleAction.clone() == toggleAction);
      assertTrue(toggleAction.commandId() == 6);
      assertTrue(toggleAction.commandName() == "6");
      assertTrue(toggleAction.name() == "Track: Toggle mute for selected tracks");
      assertTrue(toggleAction.index() >= 0);
      assertTrue(toggleAction.section() == Reaper::instance().mainSection());
      assertTrue(normalActionByIndex == normalAction, "== doesn't work");
    });

    // DONE-rust
    testWithUntil("Invoke action", [](auto testIsOver) {
      // Given
      auto action = Reaper::instance().mainSection().actionByCommandId(6);

      // When
      optional<Action> eventAction;
      int count = 0;
      Reaper::instance().actionInvoked().take_until(testIsOver).subscribe([&](Action a) {
        count++;
        eventAction = a;
      });
      action.invoke();

      // Then
      assertTrue(action.isOn());
      assertTrue(firstTrack().isMuted());
      // TODO Actually it would be nice if the actionInvoked event would be raised but it just isn't
      assertTrue(count == 0, "actionInvoked was actually raised!");
      assertTrue(!eventAction.is_initialized(), "actionInvoked was actually raised!");
    });

    // DONE-rust
    testWithUntil("Test actionInvoked event", [](auto testIsOver) {
      // Given
      auto action = Reaper::instance().mainSection().actionByCommandId(1582);

      // When
      optional<Action> eventAction;
      int count = 0;
      Reaper::instance().actionInvoked().take_until(testIsOver).subscribe([&](Action a) {
        count++;
        eventAction = a;
      });
      reaper::Main_OnCommandEx(action.commandId(), 0, nullptr);

      // Then
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventAction == action, "Action event wrong");
    });

    // DONE-rust
    testWithUntil("Unmute track", [](auto testIsOver) {
      // Given
      auto track = firstTrack();

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackMuteChanged().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      firstTrack().unmute();

      // Then
      assertTrue(!track.isMuted());
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == track, "Track event wrong");
    });

    // DONE-rust
    testWithUntil("Mute track", [](auto testIsOver) {
      // Given
      auto track = firstTrack();

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackMuteChanged().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      firstTrack().mute();

      // Then
      assertTrue(track.isMuted());
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == track, "Track event wrong");
    });

    // DONE-rust
    testWithUntil("Solo track", [](auto testIsOver) {
      // Given
      auto track = firstTrack();

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackSoloChanged().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      firstTrack().solo();

      // Then
      assertTrue(track.isSolo());
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == track, "Track event wrong");
    });

    // DONE-rust
    testWithUntil("Unsolo track", [](auto testIsOver) {
      // Given
      auto track = firstTrack();

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackSoloChanged().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      firstTrack().unsolo();

      // Then
      assertTrue(!track.isSolo());
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == track, "Track event wrong");
    });

    // DONE-rust
    test("Generate GUID", [] {
      // Given

      // When
      auto guid = Reaper::instance().generateGuid();

      // Then
      assertTrue(guid.toString().length() == 36);
    });

    // DONE-rust
    test("Main section functions", [] {
      // Given
      auto section = Reaper::instance().mainSection();

      // When

      // Then
      assertTrue(section.actions().as_blocking().count() == section.actionCount());
    });

    // DONE-rust
    test("Register and unregister action", [] {
      // Given

      // When
      // TODO Rename RegisteredAction to ActionRegistration or something like that
      auto reg = Reaper::instance().registerAction("reaPlusTest", "ReaPlus test action", [] {
      });
      auto action = Reaper::instance().actionByCommandName("reaPlusTest");

      // Then
      assertTrue(action.isAvailable());
      action.invoke();
      assertTrue(action.character() == ActionCharacter::Trigger);
      assertTrue(action.commandId() > 0);
      assertTrue(action.commandName() == "reaPlusTest");
      assertTrue(action.index() >= 0);
      assertTrue(!action.isOn());
      assertTrue(action.name() == "ReaPlus test action");
      reg.unregister();
      assertTrue(!action.isAvailable(), "Action should not be available anymore after unregistering");
    });

    // DONE-rust
    test("Register and unregister toggle action", [] {
      // Given

      // When
      bool isOn = false;
      auto reg = Reaper::instance().registerAction("reaPlusTest2", "ReaPlus test toggle action",
          [&isOn] {
            isOn = !isOn;
          },
          [&isOn] {
            return isOn;
          }
      );
      auto action = Reaper::instance().actionByCommandName("reaPlusTest2");

      // Then
      assertTrue(action.isAvailable());
      assertTrue(!action.isOn());
      action.invoke();
      assertTrue(action.isOn());
      assertTrue(action.character() == ActionCharacter::Toggle);
      assertTrue(action.commandId() > 0);
      assertTrue(action.commandName() == "reaPlusTest2");
      assertTrue(action.index() >= 0);
      assertTrue(action.name() == "ReaPlus test toggle action");
      reg.unregister();
      assertTrue(!action.isAvailable(), "Action should not be available anymore after unregistering");
    });

    auto fxTests = [this](function<Track(void)> getTrack, function<FxChain(void)> getFxChain) {
      // DONE-rust
      test("Query fx chain", [getFxChain] {
        // Given
        auto fxChain = getFxChain();

        // When

        // Then
        assertTrue(fxChain.fxCount() == 0);
        assertTrue(fxChain.fxs().as_blocking().count() == 0);
        assertTrue(fxChain.fxByIndex(0) == none);
        assertTrue(fxChain.firstFx() == none);
        assertTrue(fxChain.lastFx() == none);
        assertTrue(!fxChain.fxByGuid("bla").isAvailable());
        assertTrue(!fxChain.fxByGuidAndIndex("bla", 0).isAvailable());
        assertTrue(fxChain.firstFxByName("bla") == none);
        assertTrue(fxChain.chunk() == none);
      });

      // DONE-rust
      testWithUntil("Add track fx by original name", [getFxChain](auto testIsOver) {
        // Given
        auto fxChain = getFxChain();

        // When
        optional<Fx> eventFx;
        int count = 0;
        Reaper::instance().fxAdded().take_until(testIsOver).subscribe([&](Fx f) {
          count++;
          eventFx = f;
        });
        auto fx = fxChain.addFxByOriginalName("ReaControlMIDI (Cockos)");

        // Then
        assertTrue(fx.is_initialized());
        assertTrue(fxChain.fxCount() == 1);
        assertTrue(fxChain.fxs().as_blocking().count() == 1);
        assertTrue(fxChain.fxByIndex(0).is_initialized());
        assertTrue(*fxChain.fxByIndex(0) == *fx);
        assertTrue(fxChain.firstFx().is_initialized());
        assertTrue(*fxChain.firstFx() == *fx);
        assertTrue(fxChain.lastFx().is_initialized());
        assertTrue(*fxChain.lastFx() == *fx);
        assertTrue(!fx->guid().empty());
        assertTrue(fx->guid().length() == 36, "GUID length is wrong");
        assertTrue(fx->guid().find_first_of("{}") == string::npos);
        assertTrue(fxChain.fxByGuid(fx->guid()).isAvailable());
        assertTrue(fxChain.fxByGuid(fx->guid()) == *fx);
        assertTrue(fxChain.fxByGuidAndIndex(fx->guid(), 0).isAvailable());
        assertTrue(fxChain.fxByGuidAndIndex(fx->guid(), 1).isAvailable(),
            "Index has not automatically corrected itself");
        assertTrue(!fxChain.fxByGuidAndIndex("bla", 0).isAvailable());
        assertTrue(fxChain.firstFxByName("ReaControlMIDI (Cockos)").is_initialized());
        assertTrue(*fxChain.firstFxByName("ReaControlMIDI (Cockos)") == *fx);
        auto chainChunk = fxChain.chunk();
        assertTrue(chainChunk.is_initialized());
        assertTrue(chainChunk->startsWith("<FXCHAIN"));
        assertTrue(chainChunk->endsWith("\n>"));
        auto firstTag = chainChunk->findFirstTag(0);
        assertTrue(firstTag.is_initialized());
        assertTrue(firstTag->content() == chainChunk->content());
        assertTrue(count == 1, "Event count wrong");
        assertTrue(*eventFx == fx, "FX event wrong");
      });

      // DONE-rust
      test("Check track fx with 1 fx", [getFxChain, getTrack] {
        // Given
        auto fxChain = getFxChain();
        auto track = getTrack();

        // When
        auto fx1 = fxChain.fxByIndex(0);

        // Then
        assertTrue(fx1.is_initialized());
        assertTrue(fx1->isAvailable());
        assertTrue(fx1->index() == 0);
        assertTrue(fx1->queryIndex() == (fxChain.isInputFx() ? 0x1000000 : 0));
        assertTrue(!fx1->guid().empty());
        assertTrue(fx1->name() == "VST: ReaControlMIDI (Cockos)");
        assertTrue(fx1->chunk().startsWith("BYPASS 0 0 0"));
        assertTrue(fx1->chunk().endsWith("\nWAK 0"));
        assertTrue(fx1->tagChunk().startsWith("<VST \"VST: ReaControlMIDI (Cockos)\" reacontrolmidi"));
        assertTrue(fx1->tagChunk().endsWith("\n>"));
        assertTrue(!fx1->stateChunk().contains("<"));
        assertTrue(!fx1->stateChunk().contains(">"));
        const auto fx1Info = fx1->getFxInfo();
        assertTrue(fx1Info.getFileName().stem().string() == "reacontrolmidi");
        assertTrue(fx1Info.getTypeExpression() == "VST");
        assertTrue(fx1Info.getSubTypeExpression() == "VST");
        assertTrue(fx1Info.getEffectName() == "ReaControlMIDI");
        assertTrue(fx1Info.getVendorName() == "Cockos");
        assertTrue(fx1->track() == track);
        assertTrue(fx1->isInputFx() == fxChain.isInputFx());
        assertTrue(fx1->chain() == fxChain);
        assertTrue(fx1->parameterCount() == 17);
        assertTrue(fx1->parameters().as_blocking().count() == 17);
        assertTrue(fx1->parameterByIndex(15).isAvailable());
        assertTrue(!fx1->parameterByIndex(17).isAvailable());
        assertTrue(fx1->isEnabled());
      });

      // DONE-rust
      testWithUntil("Disable track fx", [getFxChain](auto testIsOver) {
        // Given
        auto fxChain = getFxChain();
        auto fx1 = *fxChain.fxByIndex(0);

        // When
        optional<Fx> eventFx;
        int count = 0;
        Reaper::instance().fxEnabledChanged().take_until(testIsOver).subscribe([&](Fx f) {
          count++;
          eventFx = f;
        });
        fx1.disable();

        // Then
        assertTrue(!fx1.isEnabled());
        assertTrue(count == 1, "Event count wrong");
        assertTrue(*eventFx == fx1, "FX event wrong");
      });

      // DONE-rust
      testWithUntil("Enable track fx", [getFxChain](auto testIsOver) {
        // Given
        auto fxChain = getFxChain();
        auto fx1 = *fxChain.fxByIndex(0);

        // When
        optional<Fx> eventFx;
        int count = 0;
        Reaper::instance().fxEnabledChanged().take_until(testIsOver).subscribe([&](Fx f) {
          count++;
          eventFx = f;
        });
        fx1.enable();

        // Then
        assertTrue(fx1.isEnabled());
        assertTrue(count == 1, "Event count wrong");
        assertTrue(*eventFx == fx1, "FX event wrong");
      });

      // DONE-rust
      test("Check track fx with 2 fx", [getFxChain, getTrack] {
        // Given
        auto fxChain = getFxChain();
        auto track = getTrack();

        // When
        auto fx1 = fxChain.fxByIndex(0);
        auto fx2 = fxChain.addFxByOriginalName("ReaSynth (Cockos)");

        // Then
        assertTrue(fx1.is_initialized());
        assertTrue(fx2.is_initialized());
        assertTrue(fx1->isAvailable());
        assertTrue(fx2->isAvailable());
        assertTrue(fx1->index() == 0);
        assertTrue(fx2->index() == 1);
        assertTrue(fx1->queryIndex() == (fxChain.isInputFx() ? 0x1000000 : 0));
        assertTrue(fx2->queryIndex() == (fxChain.isInputFx() ? 0x1000001 : 1));
        assertTrue(!fx1->guid().empty());
        assertTrue(!fx2->guid().empty());
        assertTrue(fx1->name() == "VST: ReaControlMIDI (Cockos)");
        assertTrue(fx2->name() == "VSTi: ReaSynth (Cockos)");
        assertTrue(fx1->chunk().startsWith("BYPASS 0 0 0"));
        assertTrue(fx1->chunk().endsWith("\nWAK 0"));
        assertTrue(fx1->tagChunk().startsWith("<VST \"VST: ReaControlMIDI (Cockos)\" reacontrolmidi"));
        assertTrue(fx1->tagChunk().endsWith("\n>"));
        assertTrue(!fx1->stateChunk().contains("<"));
        assertTrue(!fx1->stateChunk().contains(">"));
        const auto fx1Info = fx1->getFxInfo();
        const auto fx2Info = fx2->getFxInfo();
        assertTrue(fx1Info.getFileName().stem().string() == "reacontrolmidi");
        assertTrue(fx2Info.getFileName().stem().string() == "reasynth");
        assertTrue(fx1->track() == track);
        assertTrue(fx2->track() == track);
        assertTrue(fx1->isInputFx() == fxChain.isInputFx());
        assertTrue(fx2->isInputFx() == fxChain.isInputFx());
        assertTrue(fx1->chain() == fxChain);
        assertTrue(fx2->chain() == fxChain);
        assertTrue(fx1->parameterCount() == 17);
        assertTrue(fx2->parameterCount() == 15);
        assertTrue(fx1->parameters().as_blocking().count() == 17);
        assertTrue(fx1->parameterByIndex(15).isAvailable());
        assertTrue(!fx1->parameterByIndex(17).isAvailable());
        assertTrue(track.fxByQueryIndex(fxChain.isInputFx() ? 0x1000000 : 0).is_initialized());
        assertTrue(track.fxByQueryIndex(fxChain.isInputFx() ? 0x1000001 : 1).is_initialized());
        assertTrue(!track.fxByQueryIndex(fxChain.isInputFx() ? 0 : 0x1000000).is_initialized());
        assertTrue(!track.fxByQueryIndex(fxChain.isInputFx() ? 1 : 0x1000001).is_initialized());
        if (!fxChain.isInputFx()) {
          assertTrue(fxChain.firstInstrumentFx().is_initialized());
          assertTrue(fxChain.firstInstrumentFx()->index() == 1);
        }
      });

      // DONE-rust
      test("Check fx parameter", [getFxChain, getTrack] {
        // Given
        auto fxChain = getFxChain();
        auto track = getTrack();
        auto fx = *fxChain.fxByIndex(0);

        // When
        auto p = fx.parameterByIndex(5);

        // Then
        assertTrue(p.isAvailable());
        assertTrue(p.name() == "Pitch Wheel");
        assertTrue(p.index() == 5);
        assertTrue(p.character() == FxParameterCharacter::Continuous);
        assertTrue(*p.clone() == p);
        assertTrue(p.track() == track);
        assertTrue(p.parameterType() == ParameterType::FX);
        assertTrue(p.formattedValue() == "0");
        assertTrue(p.normalizedValue() == 0.5);
        assertTrue(p.reaperValue() == 0.5);
        assertTrue(p.formatNormalizedValue(p.normalizedValue()) == "0");
        assertTrue(p.fx() == fx);
        assertTrue(p.stepSize() == -1);
      });

      test("Check fx presets", [getFxChain] {
        // Given
        auto fxChain = getFxChain();
        auto fx = *fxChain.fxByIndex(0);

        // When

        // Then
        // TODO Preset count could be > 0 on some installations
        assertTrue(fx.presetCount() == 0);
        assertTrue(fx.presetName().empty());
        assertTrue(fx.presetIsDirty());
      });

      testWithUntil("Set fx parameter value", [getFxChain](auto testIsOver) {
        // Given
        auto fxChain = getFxChain();
        auto fx = *fxChain.fxByIndex(1);
        auto p = fx.parameterByIndex(5);

        // When
        optional<FxParameter> eventFxParameter;
        int count = 0;
        Reaper::instance().fxParameterValueChanged().take_until(testIsOver).subscribe([&](FxParameter f) {
          count++;
          eventFxParameter = f;
        });
        p.setNormalizedValue(0.3);

        // Then
        auto lastTouchedFxParam = Reaper::instance().lastTouchedFxParameter();
        if (fxChain.isInputFx() && Reaper::instance().getVersion() < "5.95") {
          assertTrue(!lastTouchedFxParam.is_initialized(),
              "Last touched FX param on input FX chain set in REAPER < 5.95. Impossible!");
        } else {
          assertTrue(lastTouchedFxParam.is_initialized() && *lastTouchedFxParam == p,
              "Last touched FX param incorrect");
        }
        assertTrue(p.formattedValue() == "-4.44");
        assertTrue(p.normalizedValue() == 0.30000001192092896);
        assertTrue(p.reaperValue() == 0.30000001192092896);
        assertTrue(p.formatNormalizedValue(p.normalizedValue()) == "-4.44 dB");
        // TODO 1 invocation would be better than 2
        assertTrue(count == 2, "Event count wrong (maybe 1 instead of 2, that would be an improvement?)");
        assertTrue(*eventFxParameter == p, "FX parameter event wrong");
      });

      testWithUntil("fxParameterValueChanged with heuristic fail in REAPER < 5.95", [getFxChain](auto testIsOver) {
        // Given
        auto fxChain = getFxChain();
        auto fx = *fxChain.fxByIndex(0);
        auto p = fx.parameterByIndex(0);
        p.setNormalizedValue(0.5);
        auto otherFxChain = fxChain.isInputFx() ? fx.track().normalFxChain() : fx.track().inputFxChain();
        auto fxOnOtherFxChain = otherFxChain.addFxByOriginalName("ReaControlMIDI (Cockos)");
        auto pOnOtherFxChain = fxOnOtherFxChain->parameterByIndex(0);
        // First set parameter on other FX chain to same value (confuses heuristic if fxChain is input FX chain)
        pOnOtherFxChain.setNormalizedValue(0.5);

        // When
        optional<FxParameter> eventFxParameter;
        int count = 0;
        Reaper::instance().fxParameterValueChanged().take_until(testIsOver).subscribe([&](FxParameter f) {
          count++;
          eventFxParameter = f;
        });
        p.setNormalizedValue(0.5);

        // Then
        assertTrue(count == 2, "Event count wrong (maybe 1 instead of 2, that would be an improvement?)");
        assertTrue(eventFxParameter.is_initialized(), "FX parameter event not set");
        if (fxChain.isInputFx() && Reaper::instance().getVersion() < "5.95") {
          assertTrue(*eventFxParameter != p, "FX parameter event correct in REAPER < 5.95. Impossible!");
        } else {
          assertTrue(*eventFxParameter == p, "FX parameter event wrong");
        }
      });

      testWithUntil("Move FX", [getFxChain](auto testIsOver) {
        // Given
        auto fxChain = getFxChain();
        auto midiFx = *fxChain.fxByIndex(0);
        auto synthFx = *fxChain.fxByIndex(1);

        // When
        optional<Track> eventTrack;
        int count = 0;
        Reaper::instance().fxReordered().take_until(testIsOver).subscribe([&](Track t) {
          count++;
          eventTrack = t;
        });
        fxChain.moveFx(synthFx, 0);

        // Then
        assertTrue(midiFx.index() == 1);
        assertTrue(synthFx.index() == 0);
        // TODO Detect such a programmatic FX move as well (maybe by hooking into HelperControlSurface::updateMediaTrackPositions)
        assertTrue(count == 0, "Event count wrong, maybe 1, that would be an improvement");
        // TODO Add FxChain.track()
        assertTrue(!eventTrack.is_initialized(), "Track event wrong, maybe an improvement");
      });

      testWithUntil("Remove FX", [getFxChain](auto testIsOver) {
        // Given
        auto fxChain = getFxChain();
        auto synthFx = *fxChain.fxByIndex(0);
        auto midiFx = *fxChain.fxByIndex(1);

        // When
        optional<Fx> eventFx;
        int count = 0;
        Reaper::instance().fxRemoved().take_until(testIsOver).subscribe([&](Fx f) {
          count++;
          eventFx = f;
        });
        fxChain.removeFx(synthFx);

        // Then
        assertTrue(!synthFx.isAvailable());
        assertTrue(midiFx.isAvailable());
        assertTrue(midiFx.index() == 0);
        midiFx.invalidateIndex();
        assertTrue(count == 1, "Event count wrong");
        assertTrue(*eventFx == synthFx, "FX event wrong");
      });

      testWithUntil("Add FX by chunk", [getFxChain](auto testIsOver) {
        // Given
        auto fxChain = getFxChain();
        auto fxChunk = R"foo(BYPASS 0 0 0
  <VST "VSTi: ReaSynth (Cockos)" reasynth.dll 0 "" 1919251321
  eXNlcu9e7f4AAAAAAgAAAAEAAAAAAAAAAgAAAAAAAAA8AAAAAAAAAAAAEAA=
  776t3g3wrd6mm8Q7F7fROgAAAAAAAAAAAAAAAM5NAD/pZ4g9AAAAAAAAAD8AAIA/AACAPwAAAD8AAAAA
  AAAQAAAA
  >
  FLOATPOS 0 0 0 0
  FXID {5FF5FB09-9102-4CBA-A3FB-3467BA1BFE5D}
  WAK 0)foo";

        // When
        optional<Fx> eventFx;
        int count = 0;
        Reaper::instance().fxAdded().take_until(testIsOver).subscribe([&](Fx f) {
          count++;
          eventFx = f;
        });
        auto synthFx = fxChain.addFxOfChunk(fxChunk);

        // Then
        assertTrue(synthFx.is_initialized());
        assertTrue(synthFx->index() == 1);
        assertTrue(synthFx->guid() == "5FF5FB09-9102-4CBA-A3FB-3467BA1BFE5D");
        assertTrue(synthFx->parameterByIndex(5).formattedValue() == "-6.00");
        // TODO Detect such a programmatic FX add as well (maybe by hooking into HelperControlSurface::updateMediaTrackPositions)
        assertTrue(count == 0, "Event count wrong, could be an improvement");
        assertTrue(!eventFx.is_initialized(), "FX event wrong, could be an improvement");
      });

      testWithUntil("Set fx chunk", [getFxChain](auto testIsOver) {
        // Given
        auto fxChain = getFxChain();
        auto midiFx = *fxChain.fxByIndex(0);
        auto synthFx = *fxChain.fxByIndex(1);
        auto synthFxGuidBefore = synthFx.guid();

        // When
        synthFx.setChunk(midiFx.chunk().content().to_string().c_str());

        // Then
        assertTrue(synthFx.guid() == synthFxGuidBefore, "GUIDs have changed");
        assertTrue(synthFx.name() == "VST: ReaControlMIDI (Cockos)");
        assertTrue(midiFx.index() == 0);
        assertTrue(synthFx.index() == 1);
      });

      test("Set fx tag chunk", [getFxChain] {
        // Given
        auto fxChain = getFxChain();
        auto midiFx1 = *fxChain.fxByIndex(0);
        auto midiFx2 = *fxChain.fxByIndex(1);
        auto fxTagChunk = R"foo(<VST "VSTi: ReaSynth (Cockos)" reasynth.dll 0 "" 1919251321
  eXNlcu9e7f4AAAAAAgAAAAEAAAAAAAAAAgAAAAAAAAA8AAAAAAAAAAAAEAA=
  776t3g3wrd6mm8Q7F7fROgAAAAAAAAAAAAAAAM5NAD/pZ4g9AAAAAAAAAD8AAIA/AACAPwAAAD8AAAAA
  AAAQAAAA
  >)foo";

        // When
        midiFx2.setTagChunk(fxTagChunk);

        // Then
        assertTrue(midiFx2.index() == 1);
        assertTrue(midiFx2.name() == "VSTi: ReaSynth (Cockos)");
        assertTrue(midiFx1.index() == 0);
        assertTrue(midiFx1.name() == "VST: ReaControlMIDI (Cockos)");
      });

      testWithUntil("Set fx state chunk", [getFxChain](auto testIsOver) {
        // Given
        auto fxChain = getFxChain();
        auto midiFx = *fxChain.fxByIndex(0);
        auto synthFx = *fxChain.fxByIndex(1);
        synthFx.parameterByIndex(5).setNormalizedValue(0);
        assertTrue(synthFx.parameterByIndex(5).formattedValue() != "-6.00");
        auto fxStateChunk = R"foo(eXNlcu9e7f4AAAAAAgAAAAEAAAAAAAAAAgAAAAAAAAA8AAAAAAAAAAAAEAA=
  776t3g3wrd6mm8Q7F7fROgAAAAAAAAAAAAAAAM5NAD/pZ4g9AAAAAAAAAD8AAIA/AACAPwAAAD8AAAAA
  AAAQAAAA)foo";

        // When
        synthFx.setStateChunk(fxStateChunk);

        // Then
        assertTrue(synthFx.index() == 1);
        assertTrue(synthFx.name() == "VSTi: ReaSynth (Cockos)");
        assertTrue(synthFx.parameterByIndex(5).formattedValue() == "-6.00");
        assertTrue(midiFx.index() == 0);
        assertTrue(midiFx.name() == "VST: ReaControlMIDI (Cockos)");
      });

      testWithUntil("Set fx chain chunk", [getFxChain, getTrack](auto testIsOver) {
        // Given
        auto fxChain = getFxChain();
        auto track = getTrack();
        auto otherFxChain = fxChain.isInputFx() ? track.normalFxChain() : track.inputFxChain();
        string prefix = fxChain.isInputFx() ? "<FXCHAIN" : "<FXCHAIN_REC";
        auto fxChainChunk = prefix + R"foo(
  SHOW 0
  LASTSEL 0
  DOCKED 0
  BYPASS 0 0 0
  <VST "VST: ReaControlMIDI (Cockos)" reacontrolmidi.dll 0 "" 1919118692
  ZG1jcu5e7f4AAAAAAAAAAOYAAAABAAAAAAAQAA==
  /////wAAAAAAAAAAAAAAAAkAAAAMAAAAAQAAAP8/AAAAIAAAACAAAAAAAAA1AAAAQzpcVXNlcnNcYmtsdW1cQXBwRGF0YVxSb2FtaW5nXFJFQVBFUlxEYXRhXEdNLnJlYWJhbmsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAABNYWpvcgANAAAAMTAyMDM0MDUwNjA3AAEAAAAAAAAAAAAAAAAKAAAA
  DQAAAAEAAAAAAAAAAAAAAAAAAAA=
  AAAQAAAA
  >
  FLOATPOS 0 0 0 0
  FXID {80028901-3762-477F-BE48-EA8324C178AA}
  WAK 0
  BYPASS 0 0 0
  <VST "VSTi: ReaSynth (Cockos)" reasynth.dll 0 "" 1919251321
  eXNlcu9e7f4AAAAAAgAAAAEAAAAAAAAAAgAAAAAAAAA8AAAAAAAAAAAAEAA=
  776t3g3wrd6mm8Q7F7fROgAAAAAAAAAAAAAAAM5NAD/pZ4g9AAAAAAAAAD8AAIA/AACAPwAAAD8AAAAA
  AAAQAAAA
  >
  FLOATPOS 0 0 0 0
  FXID {5FF5FB09-9102-4CBA-A3FB-3467BA1BFEAA}
  WAK 0
  >)foo";

        // When
        otherFxChain.setChunk(fxChainChunk.c_str());

        // Then
        assertTrue(otherFxChain.fxCount() == 2);
        assertTrue(fxChain.fxCount() == 2, "FX not in normal chain anymore");
      });

      test("Query fx floating window", [getFxChain] {
        // Given
        auto fxChain = getFxChain();
        auto fx = *fxChain.fxByIndex(0);

        // When

        // Then
        assertTrue(fx.floatingWindow() == nullptr);
        assertTrue(!fx.windowIsOpen());
        assertTrue(!fx.windowHasFocus());
        assertTrue(!Reaper::instance().focusedFx().is_initialized(), "Focused FX not detected");
      });

      testWithUntil("Show fx in floating window", [getFxChain](auto testIsOver) {
        // Given
        auto fxChain = getFxChain();
        auto fx = *fxChain.fxByIndex(0);

        // When
        optional<Fx> openedFx;
        int openedCount = 0;
        Reaper::instance().fxOpened().take_until(testIsOver).subscribe([&](auto f) {
          openedCount++;
          openedFx = f;
        });
        optional<Fx> focusedFx;
        int focusedCount = 0;
        Reaper::instance().fxFocused().take_until(testIsOver).subscribe([&](auto f) {
          focusedCount++;
          focusedFx = f;
        });
        fxChain.fxByIndex(0)->showInFloatingWindow();

        // Then
        assertTrue(fx.floatingWindow() != nullptr);
        assertTrue(fx.windowIsOpen());
        assertTrue(fx.windowHasFocus(), "Window has no focus");
        assertTrue(openedCount >= 1, "Incorrect opened event count", "5.95");
        assertTrue(openedFx.is_initialized() && openedFx == fx, "Incorrect opened FX event", "5.95");
        assertTrue(focusedCount == 0, "Focused FX event finally works!");
        assertTrue(!focusedFx.is_initialized(), "Focused FX event finally works!");
        const auto requestedFocusedFx = Reaper::instance().focusedFx();
        assertTrue(!requestedFocusedFx.is_initialized(), "Focused FX request finally works!");
      });

      testWithUntil("Add track JS fx by original name", [getFxChain](auto testIsOver) {
        // Given
        auto fxChain = getFxChain();

        // When
        optional<Fx> eventFx;
        int count = 0;
        Reaper::instance().fxAdded().take_until(testIsOver).subscribe([&](Fx f) {
          count++;
          eventFx = f;
        });
        auto fx = fxChain.addFxByOriginalName("phaser");

        // Then
        assertTrue(fx.is_initialized());
        assertTrue(fxChain.fxCount() == 3);
        assertTrue(fxChain.fxs().as_blocking().count() == 3);
        assertTrue(fxChain.fxByIndex(2).is_initialized());
        assertTrue(*fxChain.fxByIndex(2) == *fx);
        assertTrue(fxChain.lastFx().is_initialized());
        assertTrue(*fxChain.lastFx() == *fx);
        assertTrue(!fx->guid().empty());
        assertTrue(fxChain.fxByGuid(fx->guid()).isAvailable());
        assertTrue(!fxChain.fxByGuidAndIndex("bla", 0).isAvailable());
        assertTrue(fxChain.firstFxByName("ReaControlMIDI (Cockos)").is_initialized());
        assertTrue(*fxChain.firstFxByName("phaser") == *fx);
        assertTrue(count == 1, "Event count wrong");
        assertTrue(*eventFx == fx, "FX event wrong");
      });

      test("Query track JS fx by index", [getFxChain, getTrack] {
        // Given
        auto fxChain = getFxChain();
        auto track = getTrack();

        // When
        auto fx = fxChain.fxByIndex(2);

        // Then
        assertTrue(fx.is_initialized());
        assertTrue(fx->isAvailable());
        assertTrue(fx->index() == 2);
        assertTrue(fx->queryIndex() == (fxChain.isInputFx() ? 0x1000002 : 2));
        assertTrue(!fx->guid().empty());
        assertTrue(fx->name() == "JS: phaser");
        assertTrue(fx->chunk().startsWith("BYPASS 0 0 0"));
        assertTrue(fx->chunk().endsWith("\nWAK 0"));
        auto tagChunk = fx->tagChunk().content().to_string();
        assertTrue(fx->tagChunk().startsWith("<JS phaser \"\""));
        assertTrue(fx->tagChunk().endsWith("\n>"));
        assertTrue(!fx->stateChunk().contains("<"));
        assertTrue(!fx->stateChunk().contains(">"));
        const auto fxInfo = fx->getFxInfo();
        assertTrue(fxInfo.getFileName().stem().string() == "phaser");
        assertTrue(fx->track() == track);
        assertTrue(fx->isInputFx() == fxChain.isInputFx());
        assertTrue(fx->chain() == fxChain);
        assertTrue(fx->parameterCount() == 7);
        assertTrue(fx->parameters().as_blocking().count() == 7);
        assertTrue(fx->parameterByIndex(6).isAvailable());
        assertTrue(!fx->parameterByIndex(7).isAvailable());
      });
    };

    fxTests(
        [this] { return firstTrack(); },
        [this] { return firstTrack().normalFxChain(); }
    );

    fxTests(
        [this] { return secondTrack(); },
        [this] { return secondTrack().inputFxChain(); }
    );

    // DONE-rust
    testWithUntil("Insert track at", [](auto testIsOver) {
      // Given
      auto project = Reaper::instance().currentProject();
      auto firstTrack = *project.trackByIndex(0);
      auto secondTrack = *project.trackByIndex(1);

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackAdded().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      auto newTrack = project.insertTrackAt(1);
      newTrack.setName("Inserted track");

      // Then
      assertTrue(project.trackCount() == 4, "Track count not increased");
      assertTrue(newTrack.index() == 1, "Track index wrong");
      assertTrue(newTrack.name() == "Inserted track", "Track name wrong");
      assertTrue(secondTrack.index() == 2, "Tracks after not correctly moved");
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == newTrack, "Track event wrong");
    });

    // DONE-rust
    test("Query MIDI input devices", [] {
      // Given

      // When
      Reaper::instance().midiInputDevices();
      Reaper::instance().midiInputDeviceById(0).isAvailable();

      // Then
    });

    // DONE-rust
    test("Query MIDI output devices", [] {
      // Given

      // When
      Reaper::instance().midiOutputDevices();
      Reaper::instance().midiOutputDeviceById(0).isAvailable();

      // Then
    });

    // DONE-rust
    testAndWait("Stuff MIDI messages", [] {
      // Given
      const auto msg = MidiMessage::noteOn(0, 64, 100);
      // When
      const auto observable = Reaper::instance().incomingMidiEvents().map([](IncomingMidiEvent evt) {
        return evt.message().getType() == MidiMessageType::NoteOn
            && evt.message().getChannel() == 0
            && evt.message().getKeyNumber() == 64
            && evt.message().getVelocity() == 100
            && evt.inputDevice().id() == 62
            && Reaper::instance().sampleCounter() > 0;
      });
      Reaper::instance().stuffMidiMessage(StuffMidiMessageTarget::VirtualMidiKeyboard, msg);
      return observable;
    });

    // DONE-rust
    testWithUntil("Use undoable", [](auto testIsOver) {
      // Given
      auto t = firstTrack();

      // When
      optional<Track> eventTrack;
      int count = 0;
      Reaper::instance().trackNameChanged().take_until(testIsOver).subscribe([&](Track t) {
        count++;
        eventTrack = t;
      });
      Reaper::instance().currentProject().undoable("ReaPlus integration test operation", [t]() mutable {
        t.setName("Renamed");
      });
      const auto label = Reaper::instance().currentProject().labelOfLastUndoableAction();

      // Then
      assertTrue(t.name() == "Renamed", "Undoable operation was not executed");
      assertTrue(label && *label == "ReaPlus integration test operation", "Label was wrong");
      assertTrue(count == 1, "Event count wrong");
      assertTrue(*eventTrack == t, "Track event wrong");
    });

    // DONE-rust
    test("Undo", [] {
      // Given
      auto t = firstTrack();

      // When
      Reaper::instance().currentProject().undo();
      const auto label = Reaper::instance().currentProject().labelOfNextRedoableAction();

      // Then
      assertTrue(firstTrack().name().empty(), "Undo was not executed or undoable didn't really work");
      assertTrue(label && *label == "ReaPlus integration test operation", "Label was wrong");
    });

    // DONE-rust
    test("Redo", [] {
      // Given
      auto t = firstTrack();

      // When
      Reaper::instance().currentProject().redo();

      // Then
      assertTrue(firstTrack().name() == "Renamed", "Redo didn't work");
    });

    // DONE-rust
    test("Get REAPER window", [] {
      // Given

      // When
      auto result = Reaper::instance().mainWindow();

      // Then
      assertTrue(result == reaper::GetMainHwnd(), "Wrong main window");
    });

    // DONE-rust
    test("Mark project as dirty", [] {
      // Given

      // When
      Reaper::instance().currentProject().markAsDirty();

      // Then
    });

    // DONE-result
    test("Get project tempo", [] {
      // Given

      // When
      const auto tempo = Reaper::instance().currentProject().tempo();

      // Then
      assertTrue(tempo.bpm() == 120.0);
      assertTrue(tempo.normalizedValue() == 119.0 / 959);
    });

    // DONE-result
    testWithUntil("Set project tempo", [](auto testIsOver) {
      // Given

      // When
      int count = 0;
      Reaper::instance().masterTempoChanged().take_until(testIsOver).subscribe([&](bool) {
        count++;
      });
      Reaper::instance().currentProject().setTempo(130.0, false);

      // Then
      assertTrue(Reaper::instance().currentProject().tempo().bpm() == 130.0);
      // TODO There should be only one event invocation
      assertTrue(count == 2, "Event count wrong, but could be 1 which would be more correct");
    });

    // DONE-result
    test("Show message box", [] {
      // Given

      // When
      auto result = Reaper::instance().showMessageBox("Tests are finished", "ReaPlus", MessageBoxType::Ok);

      // Then
      assertTrue(result == MessageBoxResult::Ok);
    });

  }

  void ReaPlusIntegrationTest::test(const std::string& name, std::function<void(void)> code) {
    testInternal(name, [code](observable<bool>) {
      code();
      return observable<>::just(true);
    });
  }

  void ReaPlusIntegrationTest::testWithUntil(const std::string& name,
      std::function<void(rxcpp::observable<bool>)> code) {
    testInternal(name, [code](observable<bool> testIsOver) {
      code(testIsOver);
      return observable<>::just(true);
    });
  }
  void ReaPlusIntegrationTest::testAndWait(const std::string& name,
      std::function<rxcpp::observable<bool>(void)> code) {
    testInternal(name, [code](observable<bool>) {
      return code();
    });
  }
  void ReaPlusIntegrationTest::testInternal(const std::string& name,
      std::function<rxcpp::observable<bool>(rxcpp::observable<bool>)> code) {
    stepQueue_.push(TestStep(name, std::move(code)));
  }

  void ReaPlusIntegrationTest::processSuccess() {
    log("\nSuccessful");
  }

  void ReaPlusIntegrationTest::processFailure(const std::exception& e) {
    log("\nFailed");
    if (std::strlen(e.what()) > 0) {
      log(":");
      log(e.what());
    }
  }

  void ReaPlusIntegrationTest::logHeading(const std::string& name) {
    log("\n\n## ");
    log(name);
  }

  void ReaPlusIntegrationTest::execute() {
    Reaper::instance().clearConsole();
    stepQueue_ = {};
    log("# Testing ReaPlus");
    try {
      tests();
      executeNextStep();
    } catch (const std::exception&) {
      log("\n\nFailure while building test steps");
    }
  }
  void ReaPlusIntegrationTest::executeNextStep() {
    if (stepQueue_.empty()) {
      log("\n\nAll tests successfully executed");
    } else {
      const auto& step = stepQueue_.front();
      logHeading(step.getName());
      rxcpp::subjects::subject<bool> testIsOver;
      const auto op = step.getOperation();
      try {
        const auto testFinished = op(testIsOver.get_observable()).take(1);
        testFinished.subscribe([this, testIsOver](bool successful) {
          testIsOver.get_subscriber().on_next(true);
          if (successful) {
            processSuccess();
            stepQueue_.pop();
            Reaper::instance().executeLaterInMainThread([this] {
              executeNextStep();
            });
          } else {
            log("\nAsync result different than expected");
          }
        });
      } catch (const std::exception& e) {
        testIsOver.get_subscriber().on_next(true);
        processFailure(e);
      }
    }
  }

  void ReaPlusIntegrationTest::log(const std::string& msg) {
    Reaper::instance().showConsoleMessage(msg);
  }

  void ReaPlusIntegrationTest::assertTrue(bool expression, const std::string& errorMsg,
      const std::string& minReaperVersion) {
    if (!expression) {
      if (minReaperVersion.empty() || Reaper::instance().getVersion() >= minReaperVersion) {
        throw std::logic_error(errorMsg);
      } else {
        // This is an older REAPER version and we expect it to not work there. Just log the error.
        Reaper::instance()
            .showConsoleMessage("\nERROR because REAPER version < " + minReaperVersion + " is used: " + errorMsg);
      }
    }
  }

  Track ReaPlusIntegrationTest::firstTrack() {
    auto project = Reaper::instance().currentProject();
    return *project.firstTrack();
  }

  Track ReaPlusIntegrationTest::secondTrack() {
    auto project = Reaper::instance().currentProject();
    return *project.trackByIndex(1);
  }

  TestStep::TestStep(std::string name, TestStep::Operation operation)
      : name_(std::move(name)), operation_(std::move(operation)) {
  }
  std::string TestStep::getName() const {
    return name_;
  }
  TestStep::Operation TestStep::getOperation() const {
    return operation_;
  }
}

max_stars_repo_name: helgoboss/reaplus

max_issues_repo_name: helgoboss/reaplus

max_forks_repo_name: helgoboss/reaplus


text:
#include <string>
#include <vector>
#include <map>
#include <set>
#include <iostream>
#include <unistd.h>
#include <sstream>
#include <fstream>
#include <algorithm>
#include <regex>
#include <cstdlib>
#include "Read.hpp"
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <zlib.h>

namespace io = boost::iostreams;

using namespace std;

int main(int argc, char** argv)
{
	bool exit = false;
    int opt;
    int min_reads = 1000;
    char group_level = 's';
    string fastqFile, outputFileName, taxonomy_file,line;
    while ((opt = getopt(argc,argv,"i:o:m:x:r:")) != EOF)
    {
        switch(opt)
        {
            case 'i': fastqFile = optarg; break;
            case 'o': outputFileName = optarg; break;
            case 'm': taxonomy_file = optarg; break;
            case 'x': group_level = optarg[0]; break;
            case 'r': min_reads = atoi(optarg); break;
            case '?': exit = true; break;
            default: exit=true;
        }
    }
    if (optind < argc) {
        exit = true;
        cerr << "Non-option argument: ";
        while (optind < argc)
            cerr << argv[optind++];
        cerr << endl;
    }
    if(argc == 1){
        exit = true;
    }
    if(exit){
        cerr << "\nUsage: SplitFastq\n -i InputFastq\n -o Output Path and Prefix\n -m mpa taxonomy file\n -r (optional) minimum number of reads needed for species to be considered [1000]\n (optional) -x group_level [s: species (default), g: genus, f: family, o: order, c: class, p: phylum, d: domain]\n\n";
        return 0;
    }

    cerr << "Reading in MPA File" << endl;
    ifstream inputFile_mpa(taxonomy_file);
    map<string, Read> read_classification;
    set<string> classification_set;
    map<string, int> classification_count;
    while (getline(inputFile_mpa, line))
    {
        istringstream lin(line);
        string id, classification, level;
        lin >> id;
        getline(lin, classification);
        stringstream  data(classification);
        vector<string> result;
        while(getline(data,level,'|'))
        {
            result.push_back(level);
        }
        string tmp = "";
        if(read_classification.count(id) == 0){
        	Read tmp_read = Read(id, 0);
            read_classification.insert(make_pair(id, tmp_read));
            read_classification.at(id).setTaxonomy(result);
            tmp = read_classification.at(id).getClassificationForFileName(group_level);
        }
        if(tmp != ""){
        	classification_set.insert(tmp);
            if(classification_count.count(tmp) == 0){
                classification_count.insert(make_pair(tmp, 0));
            }
            classification_count[tmp]++;
        }
    }

    // Read in the input fastq
    io::filtering_istream in_filter;
    in_filter.push(io::gzip_decompressor());
    in_filter.push(io::file_source(fastqFile));

	//ifstream inputFile_filter(file_name);
    set<string> to_drop;
    cerr << "Reading in fastq" << endl;
    int count = 0;
    int seen = 0;
    // Need a list of output handles to write too
    map<string, string> output_handles;
    // Create a handle for each classificaion in classification_set
    for (set<string>::iterator it = classification_set.begin(); it != classification_set.end(); ++it)
    {
        if(classification_count[*it] >= min_reads){
            output_handles.insert(make_pair(*it, outputFileName + "_" + *it +".fasta.gz"));
        }
    }

    string id, seq;
    map<string, string> read_seq;
    map<string, string> read_levels;
	while (getline(in_filter, line, '\n'))
	{
		if(count == 0){
			istringstream lin(line);
			// Only want the read ID, not anything else in the header
			lin >> id;
            //cerr << id << endl;
			count++;
			continue;
		} else if(count == 1){
			seq	= line;
			count++;
			continue;
		} else if(count == 2){
			count++;
			continue;
		} else if(count == 3){
			count = 0;
			seen++;
		}
		if(seen % 10000 == 0){
			cerr << "Processed " << seen << " reads" << endl;
		}
		// Need to get the species for this read now that we have read it in properly
		// ignore any read that doesn't have a classification
		if(read_classification.count(id.substr(1)) == 0){
			continue;
		}
        read_seq.insert(make_pair(id.substr(1), seq));
		string level = read_classification.at(id.substr(1)).getClassificationForFileName(group_level);
        read_levels.insert(make_pair(id.substr(1), level));
    }
    cerr << "Outputting Reads" << endl;
    map<string, set<string>> read_groups;
    for(map<string, string>::iterator it = output_handles.begin(); it != output_handles.end(); ++it)
    {
        set<string> tmp;
        read_groups.insert(make_pair(it->first, tmp));
        count = 0;
        for(map<string, string>::iterator it2 = read_levels.begin(); it2 != read_levels.end(); ++it2){
            if(it2->second == it->first || (it2->second == "" && read_classification.at(it2->first).parentLevel(it->first))){
                read_groups[it->first].insert(it2->first);
                count ++;
            }
        }
        cerr << it->first << " " << count << endl;
    }
    for(map<string, string>::iterator it = output_handles.begin(); it != output_handles.end(); ++it)
    {
        if(read_groups[it->first].size() > 0){
            gzFile tmp_file = gzopen(it->second.c_str(),"ab");
            for(set<string>::iterator it2 = read_groups[it->first].begin(); it2 != read_groups[it->first].end(); ++it2){
                string val = ">" + *it2 +"\n";
                gzprintf(tmp_file,val.c_str());
                val = read_seq[*it2]+"\n";
                gzprintf(tmp_file,val.c_str());
            }
            gzclose_w(tmp_file);
        }
    }

	cerr << "Completing Output" << endl;
	ofstream tmp;
    tmp.open(outputFileName + "_classification_list.txt");
	for (map<string, string>::iterator it = output_handles.begin(); it != output_handles.end(); ++it)
    {
    	tmp << outputFileName << "_" << it->first << "\n";
    	//string str = "gzip ";
    	//str = str + it->second;
    	//const char *command = str.c_str();
    	//system(command);
    }
    tmp.close();

	return 0;
}

ext: cpp


text:
/* Copyright (C) 2012-2017 IBM Corp.
 * This program is 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. See accompanying LICENSE file.
 */

/* Test_Replicate.cpp - Testing the functionality of replicating one
 * slot from a vector acress the whole vector (or replicating each slot
 * to a full cipehrtext)
 */

#include <cassert>
#include <NTL/lzz_pXFactoring.h>
NTL_CLIENT

#include "FHE.h"
#include "replicate.h"
#include "timing.h"
#include "ArgMap.h"

static bool noPrint = false;

static bool check_replicate(const Ctxt& c1, const Ctxt& c0, long i,
			    const FHESecKey& sKey, const EncryptedArray& ea)
{
  PlaintextArray pa0(ea), pa1(ea);
  ea.decrypt(c0, sKey, pa0);
  ea.decrypt(c1, sKey, pa1);
  replicate(ea, pa0, i);

  return equals(ea, pa1, pa0); // returns true if replication succeeded
}

class StopReplicate { };

// A class that handles the replicated ciphertexts one at a time
class ReplicateTester : public ReplicateHandler {
public:
  const FHESecKey& sKey;
  const EncryptedArray& ea;
  const PlaintextArray& pa;
  long B;

  double t_last, t_total;
  long pos;
  bool error;

  ReplicateTester(const FHESecKey& _sKey, const EncryptedArray& _ea,
                  const PlaintextArray& _pa, long _B)
  : sKey(_sKey), ea(_ea), pa(_pa), B(_B)
  {
    t_last = GetTime();
    t_total = 0.0;
    pos = 0;
    error = false;
  }

  // This method is called for every replicated ciphertext: in the i'th time
  // that it is called, the cipehrtext will have in all the slots the content
  // of the i'th input slot. In this test program we only decrypt and check
  // the result, in a real program it will do something with the cipehrtext.
  virtual void handle(const Ctxt& ctxt) {

    double t_new = GetTime();
    double t_elapsed = t_new - t_last;
    t_total += t_elapsed;

    // Decrypt and check
    PlaintextArray pa1 = pa;
    replicate(ea, pa1, pos);
    PlaintextArray pa2(ea);

    if (pos==0 && !noPrint) CheckCtxt(ctxt, "replicateAll");

    ea.decrypt(ctxt, sKey, pa2);
    if (!equals(ea, pa1, pa2)) error = true; // record the error, if any
    t_last = GetTime();

    pos++;
    if (B > 0 && pos >= B) throw StopReplicate();
  }
};

void  TestIt(long m, long p, long r, long d, long L, long bnd, long B)
{
  if (!noPrint)
    std::cout << "*** TestIt" << (isDryRun()? "(dry run):" : ":")
	 << " m=" << m
	 << ", p=" << p
	 << ", r=" << r
	 << ", d=" << d
	 << ", L=" << L
	 << ", bnd=" << bnd
	 << ", B=" << B
	 << endl;

  setTimersOn();
  FHEcontext context(m, p, r);
  buildModChain(context, L, /*c=*/2);

  ZZX G;
  if (d == 0)
    G = context.alMod.getFactorsOverZZ()[0];
  else
    G = makeIrredPoly(p, d);

  if (!noPrint) {
    context.zMStar.printout();
    cout << endl;
    cout << "G = " << G << "\n";
  }

  FHESecKey secretKey(context);
  const FHEPubKey& publicKey = secretKey;
  secretKey.GenSecKey(); // A +-1/0 secret key
  addSome1DMatrices(secretKey); // compute key-switching matrices that we need

  EncryptedArray ea(context, G);
  PlaintextArray xp0(ea), xp1(ea);
  random(ea, xp0);
  random(ea, xp1);

  Ctxt xc0(publicKey);
  ea.encrypt(xc0, publicKey, xp0);

  ZZX poly_xp1;
  ea.encode(poly_xp1, xp1);

  if (!noPrint)  cout << "** Testing replicate():\n";
  bool error = false;
  Ctxt xc1 = xc0;
  if (!noPrint) CheckCtxt(xc1, "before replicate");
  replicate(ea, xc1, ea.size()/2);
  if (!check_replicate(xc1, xc0, ea.size()/2, secretKey, ea)) error = true;
  if (!noPrint) CheckCtxt(xc1, "after replicate");

  // Get some timing results
  for (long i=0; i<20 && i<ea.size(); i++) {
    xc1 = xc0;
    FHE_NTIMER_START(replicate);
    replicate(ea, xc1, i);
    if (!check_replicate(xc1, xc0, i, secretKey, ea)) error = true;
    FHE_NTIMER_STOP(replicate);
  }
  cout << (error? "BAD" : "GOOD") << endl;

  if (!noPrint) {
    printAllTimers();
    cout << "\n** Testing replicateAll()... " << std::flush;
  }
#ifdef DEBUG_PRINTOUT
  replicateVerboseFlag = true;
#else
  replicateVerboseFlag = false;
#endif

  error = false;
  ReplicateTester *handler = new ReplicateTester(secretKey, ea, xp0, B);
  try {
    FHE_NTIMER_START(replicateAll);
    replicateAll(ea, xc0, handler, bnd);
  }
  catch (StopReplicate) {
  }
  std::cout << (handler->error? "BAD" : "GOOD") << endl;
  if (!noPrint)
    cout << "  total time=" << handler->t_total << " ("
         << ((B>0)? B : ea.size()) << " vectors)\n";
  delete handler;
}

int main(int argc, char *argv[])
{
  ArgMap amap;

  bool dry=false;
  amap.arg("dry", dry, "dry=1 for a dry-run");

  long m=2047;
  amap.arg("m", m, "cyclotomic ring");

  long p=2;
  amap.arg("p", p, "plaintext base");

  long r=1;
  amap.arg("r", r,  "lifting");

  long d=1;
  amap.arg("d", d, "degree of the field extension");
  amap.note("d == 0 => factors[0] defines extension");

  long L=250;
  amap.arg("L", L, "# of bits in the modulus chain");

  long bnd = 64;
  amap.arg("bnd", bnd, "recursion bound for replication");

  long B = 0;
  amap.arg("B", B, "bound for # of replications", "all");

  amap.arg("noPrint", noPrint, "suppress printouts");

  amap.parse(argc, argv);
  setDryRun(dry);

  TestIt(m, p, r, d, L, bnd, B);
  cout << endl;
}

max_stars_repo_name: pememoni/HElib

max_issues_repo_name: PNIDEMOOO/HElib

max_forks_repo_name: PNIDEMOOO/HElib


text:
//
//  Scheduler.h
//
//
//  Created by Ben Lengerich on 1/25/16.
//
//

#ifndef Scheduler_hpp
#define Scheduler_hpp

#include <Eigen/Dense>
#include <memory>
#include <unordered_map>
#include <uv.h>
#include <vector>

#ifdef BAZEL
#include "Algorithms/Algorithm.hpp"
#include "Algorithms/AlgorithmOptions.hpp"
#include "Models/ModelOptions.hpp"
#include "Scheduler/Job.hpp"
#include "gtest/gtest_prod.h"
#else
#include "../Algorithms/Algorithm.hpp"
#include "../Algorithms/AlgorithmOptions.hpp"
#include "Job.hpp"
#include "../Models/ModelOptions.hpp"
#endif

using namespace std;

class Scheduler {
/* Class to run jobs and get information about the currently running jobs.
*/

public:
	job_id_t newJob(const JobOptions_t&);
	// Assumes that algorithm and job have been created by this scheduler.
	// Simply packages as a job and assigns an ID.
	// Returns the job's assigned ID, 0 for failure.

	bool setMetaData(const job_id_t, const string&, vector<string>&);
	// Sets the filename and marker_ids for the job at the given job_id
	// returns true on success, false otherwise

	bool setX(const job_id_t, const Eigen::MatrixXf&);
	// Sets the X matrix for the job at the given job_id.
	// returns true on success, false otherwise.

	bool setY(const job_id_t, const Eigen::MatrixXf&);
	// Sets the Y matrix for the job at the given job_id.
	// returns true on success, false otherwise.

    bool imputation(const job_id_t);

	bool setModelAttributeMatrix(const job_id_t, const string&, Eigen::MatrixXf*);
	// Sets a matrix with the given name for the job at the given job_id.
	// returns true on success, false otherwise.

	bool startJob(const job_id_t, void (*f)(uv_work_t*, int));
	// Queues the job at the given job_id, with the callback to be called when the job is completed.
	// Returns true if successfully queued, false otherwise.

	float checkJobProgress(const job_id_t);
	// Returns the progress of the job or -1 on failure.

	bool cancelJob(const job_id_t);
	// Cancels a potentially running job.
	// Returns True is the job was successfully sent a shutdown signal, false otherwise.

	shared_ptr<Model> getModel(const model_id_t);
	shared_ptr<Algorithm> getAlgorithm(const algorithm_id_t);
	shared_ptr<Job_t> getJob(const job_id_t);

	MatrixXf getJobResult(const job_id_t);

	// TODO: How to know if the user owns the algorithm?
	bool deleteJob(const job_id_t);

	static Scheduler& Instance();
	// This class follows the singleton pattern.

protected:
	// Singleton constructors must be protected or private
	Scheduler();
	Scheduler(Scheduler const&){};
	Scheduler& operator=(Scheduler const&);

private:
	algorithm_id_t newAlgorithm(const AlgorithmOptions_t&);
	// returns the algorithm's assigned ID, 0 for failure.

	model_id_t newModel(const ModelOptions_t&);
	// returns the model's assigned ID, 0 for failure.

	algorithm_id_t getNewAlgorithmId();
	model_id_t getNewModelId();
	job_id_t getNewJobId();
	// Generates and returns new identifier. Failure is identified with 0.

	// Checks whether an ID fits in the range used to index algorithms, models, or job.
	bool ValidAlgorithmId(const algorithm_id_t);
	bool ValidModelId(const model_id_t);
	bool ValidJobId(const job_id_t);

	// Checks whether an Id is currently being used. Returns true for currently in use, false otherwise.
	bool AlgorithmIdUsed(const algorithm_id_t);
	bool ModelIdUsed(const model_id_t);
	bool JobIdUsed(const job_id_t);

	bool deleteAlgorithm(const algorithm_id_t);
	bool deleteModel(const model_id_t);

    #ifdef BAZEL
    FRIEND_TEST(SchedulerTest, newAlgorithm);
    FRIEND_TEST(SchedulerTest, newModel);
    FRIEND_TEST(SchedulerTest, getNewAlgorithmId);
    FRIEND_TEST(SchedulerTest, getNewModelId);
    FRIEND_TEST(SchedulerTest, getNewJobId);
    FRIEND_TEST(SchedulerTest, ValidAlgorithmId);
    FRIEND_TEST(SchedulerTest, ValidModelId);
    FRIEND_TEST(SchedulerTest, ValidJobId);
    FRIEND_TEST(SchedulerTest, AlgorithmIdUsed);
    FRIEND_TEST(SchedulerTest, ModelIdUsed);
    FRIEND_TEST(SchedulerTest, JobIdUsed);
    #endif

    const unsigned int kMaxThreads = 5;

    const algorithm_id_t kMaxAlgorithmId = 100;
    algorithm_id_t next_algorithm_id;

    const model_id_t kMaxModelId = 100;
    model_id_t next_model_id;

    const job_id_t kMaxJobId = 100;
    job_id_t next_job_id;

    unordered_map<algorithm_id_t, shared_ptr<Algorithm>> algorithms_map;
    unordered_map<model_id_t, shared_ptr<Model>> models_map;
    unordered_map<job_id_t, shared_ptr<Job_t>> jobs_map;
    // Maps to track all jobs (running, waiting, and completed). indexed by job_id.
};

void trainAlgorithmThread(uv_work_t* req);

#endif /* Scheduler_hpp */

ext: hpp


text:
/*=============================================================================
    Copyright (c) 2003 Jonathan de Halleux (dehalleux@pelikhan.com)
    http://spirit.sourceforge.net/

  Distributed under the Boost Software License, Version 1.0. (See accompanying
  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
#ifndef BOOST_SPIRIT_ACTOR_PUSH_BACK_ACTOR_HPP
#define BOOST_SPIRIT_ACTOR_PUSH_BACK_ACTOR_HPP

#include <boost/spirit/home/classic/namespace.hpp>
#include <boost/spirit/home/classic/actor/ref_value_actor.hpp>
#include <boost/spirit/home/classic/actor/ref_const_ref_actor.hpp>

namespace boost { namespace spirit {

BOOST_SPIRIT_CLASSIC_NAMESPACE_BEGIN

    ///////////////////////////////////////////////////////////////////////////
    //  Summary:
    //
    //  A semantic action policy that appends a value to the back of a
    //  container.
    //  (This doc uses convention available in actors.hpp)
    //
    //  Actions (what it does and what ref, value_ref must support):
    //      ref.push_back( value );
    //      ref.push_back( T::value_type(first,last) );
    //      ref.push_back( value_ref );
    //
    //  Policy name:
    //      push_back_action
    //
    //  Policy holder, corresponding helper method:
    //      ref_value_actor, push_back_a( ref );
    //      ref_const_ref_actor, push_back_a( ref, value_ref );
    //
    //  () operators: both
    //
    //  See also ref_value_actor and ref_const_ref_actor for more details.
    ///////////////////////////////////////////////////////////////////////////
    struct push_back_action
    {
        template<
            typename T,
            typename ValueT
        >
        void act(T& ref_, ValueT const& value_) const
        {
            ref_.push_back( value_ );
        }
        template<
            typename T,
            typename IteratorT
        >
        void act(
            T& ref_,
            IteratorT const& first_,
            IteratorT const& last_
            ) const
        {
            typedef typename T::value_type value_type;
            value_type value(first_,last_);

            ref_.push_back( value );
        }
    };

//  Deprecated interface. Use push_back_a
    template<typename T>
    inline ref_value_actor<T,push_back_action>
    append(T& ref_)
    {
        return ref_value_actor<T,push_back_action>(ref_);
    }

    template<typename T>
    inline ref_value_actor<T,push_back_action>
    push_back_a(T& ref_)
    {
        return ref_value_actor<T,push_back_action>(ref_);
    }

    template<
        typename T,
        typename ValueT
    >
    inline ref_const_ref_actor<T,ValueT,push_back_action>
    push_back_a(
        T& ref_,
        ValueT const& value_
    )
    {
        return ref_const_ref_actor<T,ValueT,push_back_action>(ref_,value_);
    }

BOOST_SPIRIT_CLASSIC_NAMESPACE_END

}}

#endif

max_stars_repo_name: randolphwong/mcsema

max_issues_repo_name: randolphwong/mcsema

max_forks_repo_name: randolphwong/mcsema


text:
#include "Polynomial.h"
#include <Eigen/Core>
#include <random>
#include "testUtil.h"
#include <iostream>
#include <typeinfo>

using namespace Eigen;
using namespace std;

template <typename CoefficientType>
void testIntegralAndDerivative() {
  VectorXd coefficients = VectorXd::Random(5);
  Polynomial<CoefficientType> poly(coefficients);

  cout << poly << endl;

  cout << "derivative: " << poly.derivative(1) << endl;

  Polynomial<CoefficientType> third_derivative = poly.derivative(3);

  cout << "third derivative: " << third_derivative << endl;
  Polynomial<CoefficientType> third_derivative_check = poly.derivative().derivative().derivative();
  valuecheckMatrix(third_derivative.getCoefficients(), third_derivative_check.getCoefficients(), 1e-14);

  Polynomial<CoefficientType> tenth_derivative = poly.derivative(10);
  valuecheckMatrix(tenth_derivative.getCoefficients(), VectorXd::Zero(1), 1e-14);

  Polynomial<CoefficientType> integral = poly.integral(0.0);
  cout << "integral: " << integral << endl;
  Polynomial<CoefficientType> poly_back = integral.derivative();
  valuecheckMatrix(poly_back.getCoefficients(), poly.getCoefficients(), 1e-14);
}

template <typename CoefficientType>
void testOperators() {
  int max_num_coefficients = 6;
  int num_tests = 10;
  default_random_engine generator;
  std::uniform_int_distribution<> int_distribution(1, max_num_coefficients);
  uniform_real_distribution<double> uniform;

  for (int i = 0; i < num_tests; ++i) {
    VectorXd coeff1 = VectorXd::Random(int_distribution(generator));
    Polynomial<CoefficientType> poly1(coeff1);

    VectorXd coeff2 = VectorXd::Random(int_distribution(generator));
    Polynomial<CoefficientType> poly2(coeff2);

    double scalar = uniform(generator);

    cout << "-------" << endl;
    cout << "p1 = " << poly1 << endl;
    cout << "p2 = " << poly2 << endl;
    cout << "c = " << scalar << endl;

    Polynomial<CoefficientType> sum = poly1 + poly2;
    cout << "p1+p2: " << sum << endl;
    Polynomial<CoefficientType> difference = poly2 - poly1;
    cout << "p2-p1: " << difference << endl;
    Polynomial<CoefficientType> product = poly1 * poly2;
    cout << "p1*p2: " << product << endl;
    Polynomial<CoefficientType> poly1_plus_scalar = poly1 + scalar;
    cout << "p1+c: " << poly1_plus_scalar << endl;
    Polynomial<CoefficientType> poly1_minus_scalar = poly1 - scalar;
    cout << "p1-c: " << poly1_minus_scalar << endl;
    Polynomial<CoefficientType> poly1_scaled = poly1 * scalar;
    cout << "p1*c: " << poly1_scaled << endl;
    Polynomial<CoefficientType> poly1_div = poly1 / scalar;
    cout << "p1/c: " << poly1_div << endl;
    Polynomial<CoefficientType> poly1_times_poly1 = poly1;
    poly1_times_poly1 *= poly1_times_poly1;
    cout << "p1*p1" << poly1_times_poly1 << endl;

    double t = uniform(generator);
    valuecheck(sum.value(t), poly1.value(t) + poly2.value(t), 1e-8);
    valuecheck(difference.value(t), poly2.value(t) - poly1.value(t), 1e-8);
    valuecheck(product.value(t), poly1.value(t) * poly2.value(t), 1e-8);
    valuecheck(poly1_plus_scalar.value(t), poly1.value(t) + scalar, 1e-8);
    valuecheck(poly1_minus_scalar.value(t), poly1.value(t) - scalar, 1e-8);
    valuecheck(poly1_scaled.value(t), poly1.value(t) * scalar, 1e-8);
    valuecheck(poly1_div.value(t), poly1.value(t) / scalar, 1e-8);
    valuecheck(poly1_times_poly1.value(t), poly1.value(t) * poly1.value(t), 1e-8);
  }
}

template <typename CoefficientType>
void testRoots() {
  int max_num_coefficients = 6;
  default_random_engine generator;
  std::uniform_int_distribution<> int_distribution(1, max_num_coefficients);

  int num_tests = 50;
  for (int i = 0; i < num_tests; ++i) {
    VectorXd coeffs = VectorXd::Random(int_distribution(generator));
    Polynomial<CoefficientType> poly(coeffs);
    auto roots = poly.roots();
    valuecheck<DenseIndex>(roots.rows(), poly.getDegree());
    for (int i = 0; i < roots.size(); i++) {
      auto value = poly.value(roots[i]);
      valuecheck(std::abs(value), 0.0, 1e-8);
    }
  }
}

void testEvalType() {
  int max_num_coefficients = 6;
  default_random_engine generator;
  std::uniform_int_distribution<> int_distribution(1, max_num_coefficients);
  VectorXd coeffs = VectorXd::Random(int_distribution(generator));
  Polynomial<double> poly(coeffs);

  auto valueIntInput = poly.value(1);
  valuecheck(typeid(decltype(valueIntInput)) == typeid(double), true);

  auto valueComplexInput = poly.value(std::complex<double>(1.0, 2.0));
  valuecheck(typeid(decltype(valueComplexInput)) == typeid(std::complex<double>), true);
}

template <typename CoefficientType>
void testPolynomialMatrix() {
  int max_matrix_rows_cols = 7;
  int num_coefficients = 6;
  default_random_engine generator;

  uniform_int_distribution<> matrix_size_distribution(1, max_matrix_rows_cols);
  int rows_A = matrix_size_distribution(generator);
  int cols_A = matrix_size_distribution(generator);
  int rows_B = cols_A;
  int cols_B = matrix_size_distribution(generator);

  auto A = Polynomial<CoefficientType>::randomPolynomialMatrix(num_coefficients, rows_A, cols_A);
  auto B = Polynomial<CoefficientType>::randomPolynomialMatrix(num_coefficients, rows_B, cols_B);
  auto C = Polynomial<CoefficientType>::randomPolynomialMatrix(num_coefficients, rows_A, cols_A);
  auto product = A * B; // just verify that this is possible without crashing
  auto sum = A + C;

  uniform_real_distribution<double> uniform;
  for (int row = 0; row < A.rows(); ++row) {
    for (int col = 0; col < A.cols(); ++col) {
      double t = uniform(generator);
      valuecheck(sum(row, col).value(t), A(row, col).value(t) + C(row, col).value(t), 1e-8);
    }
  }

  C.setZero(); // this was a problem before
}

int main(int argc, char **argv) {
  testIntegralAndDerivative<double>();
  testOperators<double>();
  testRoots<double>();
  testEvalType();
  testPolynomialMatrix<double>();
  return 0;
}


text:
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <config/command_socket.h>
#include <config/command_mgr.h>
#include <config/config_log.h>
#include <dhcp/iface_mgr.h>
#include <boost/bind.hpp>
#include <unistd.h>

namespace isc {
namespace config {

ConnectionSocket::ConnectionSocket(int sockfd) {
    sockfd_ = sockfd;

    // Install commandReader callback. When there's any data incoming on this
    // socket, commandReader will be called and process it. It may also
    // eventually close this socket.
    isc::dhcp::IfaceMgr::instance().addExternalSocket(sockfd,
        boost::bind(&ConnectionSocket::receiveHandler, this));
    }

void ConnectionSocket::close() {
    LOG_INFO(command_logger, COMMAND_SOCKET_CONNECTION_CLOSED).arg(sockfd_);

    // Unregister this callback
    isc::dhcp::IfaceMgr::instance().deleteExternalSocket(sockfd_);

    // We're closing a connection, not the whole socket. It's ok to just
    // close the connection and don't delete anything.
    ::close(sockfd_);
}

void ConnectionSocket::receiveHandler() {
    CommandMgr::instance().commandReader(sockfd_);
}

};
};

ext: cc

max_stars_repo_name: sebschrader/debian-pkg-isc-kea

max_issues_repo_name: sebschrader/debian-pkg-isc-kea

max_forks_repo_name: sebschrader/debian-pkg-isc-kea