<commit_before>/*
 * Copyright (c) 2014, Alex Nichol and Alux contributors.
 * All rights reserved.

 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "map-creator-x64.h"

namespace OS {

void MapCreator::IncrementPhysOffset() {
  // find the region in which the current offset lies
  MemoryRegion * reg = regions->FindRegion(physOffset);
  if (!reg) {
    Panic("MapCreator::IncrementPhysOffset() - physOffset out of bounds.");
  }
  physOffset += 0x1000;
  while (physOffset + 0x1000 > reg->GetEnd()) {
    reg = regions->NextRegion(reg);
    if (!reg) {
      Panic("MapCreator::IncrementPhysOffset() - out of ranges.");
    }
    physOffset = reg->GetStart();
  }
}

void MapCreator::InitializeTables() {
  pdtOffset = 0;
  pdptOffset = 0;

  uint64_t * physPDPT, * physPML4;
  AllocatePage(&physPML4, NULL);
  AllocatePage(&physPDPT, NULL);
  AllocatePage(&physPDT, &virtPDT);
  pdpt = (PhysAddr)physPDPT;
  pml4 = (PhysAddr)physPML4;

  physPML4[0] = pdpt | 3;
  physPDPT[0] = (uint64_t)physPDT | 3;
}

void MapCreator::MapNextPage() {
  if (pdtOffset == 0x200) {
    pdtOffset = 0;
    pdptOffset++;
    AllocatePage(&physPDT, &virtPDT);
    VisiblePDPT()[pdptOffset] = (uint64_t)physPDT | 3;
  }
  VisiblePDT()[pdtOffset++] = virtMapped | 3;
  virtMapped += 0x200000;
}

void MapCreator::Switch() {
  hasSwitched = true;
  __asm__("mov %%cr3, %0" : : "r" (pml4));
  cout << "switched to new address space" << endl;
}

void MapCreator::AllocatePage(uint64_t ** phys, uint64_t ** virt) {
  if (virt) *virt = (uint64_t *)virtOffset;
  if (phys) *phys = (uint64_t *)physOffset;
  IncrementPhysOffset();
  bzero((void *)virtOffset, 0x1000);
  virtOffset += 0x1000;
}

uint64_t * MapCreator::VisiblePDT() {
  if (!hasSwitched) {
    return physPDT;
  }
  return virtPDT;
}

uint64_t * MapCreator::VisiblePDPT() {
  return (uint64_t *)pdpt;
}

MapCreator::MapCreator(PhysRegionList * r, AllocatorList * a) {
  regions = r;
  allocators = a;
  virtMapped = 0;
  hasSwitched = false;
}

void MapCreator::Map(uintptr_t total, uintptr_t current) {
  assert(!(total & 0x1fffff));
  assert(!(current & 0x1fffff));

  virtOffset = current;
  physOffset = current;

  InitializeTables();

  while (virtMapped < total) {

    MapNextPage();
    if (virtMapped >= virtOffset + 0x1000 && !hasSwitched) {
      Switch();
    }

  }

  if (!hasSwitched) {
    Switch();
  }

  cout << "mapped a total of " << virtMapped << " bytes" << endl;

  // create our scratch space
  uint64_t * physScratchPDT, * physScratchPT;
  uint64_t * virtScratchPDT;
  AllocatePage(&physScratchPDT, &virtScratchPDT);
  AllocatePage(&physScratchPT, &virtScratchPT);
  virtScratchPDT[0x1ff] = (uint64_t)physScratchPT | 3;
  VisiblePDPT()[0x1ff] = (uint64_t)physScratchPDT | 3;

  // mark space as used
  MemoryRegion region(0, physOffset);
  allocators->Reserve(region);
}

uint64_t * MapCreator::ScratchPageTable() {
  return virtScratchPT;
}

PhysAddr MapCreator::GetPML4() {
  return pml4;
}

PhysAddr MapCreator::GetPDPT() {
  return pdpt;
}

}

<commit_msg>it's because i need to GenerateAllocators()<commit_after>/*
 * Copyright (c) 2014, Alex Nichol and Alux contributors.
 * All rights reserved.

 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "map-creator-x64.h"

namespace OS {

void MapCreator::IncrementPhysOffset() {
  // find the region in which the current offset lies
  MemoryRegion * reg = regions->FindRegion(physOffset);
  if (!reg) {
    Panic("MapCreator::IncrementPhysOffset() - physOffset out of bounds.");
  }
  physOffset += 0x1000;
  while (physOffset + 0x1000 > reg->GetEnd()) {
    reg = regions->NextRegion(reg);
    if (!reg) {
      Panic("MapCreator::IncrementPhysOffset() - out of ranges.");
    }
    physOffset = reg->GetStart();
  }
}

void MapCreator::InitializeTables() {
  pdtOffset = 0;
  pdptOffset = 0;

  uint64_t * physPDPT, * physPML4;
  AllocatePage(&physPML4, NULL);
  AllocatePage(&physPDPT, NULL);
  AllocatePage(&physPDT, &virtPDT);
  pdpt = (PhysAddr)physPDPT;
  pml4 = (PhysAddr)physPML4;

  physPML4[0] = pdpt | 3;
  physPDPT[0] = (uint64_t)physPDT | 3;
}

void MapCreator::MapNextPage() {
  if (pdtOffset == 0x200) {
    pdtOffset = 0;
    pdptOffset++;
    AllocatePage(&physPDT, &virtPDT);
    VisiblePDPT()[pdptOffset] = (uint64_t)physPDT | 3;
  }
  VisiblePDT()[pdtOffset++] = virtMapped | 3;
  virtMapped += 0x200000;
}

void MapCreator::Switch() {
  hasSwitched = true;
  __asm__("mov %%cr3, %0" : : "r" (pml4));
  cout << "switched to new address space" << endl;
}

void MapCreator::AllocatePage(uint64_t ** phys, uint64_t ** virt) {
  if (virt) *virt = (uint64_t *)virtOffset;
  if (phys) *phys = (uint64_t *)physOffset;
  IncrementPhysOffset();
  bzero((void *)virtOffset, 0x1000);
  virtOffset += 0x1000;
}

uint64_t * MapCreator::VisiblePDT() {
  if (!hasSwitched) {
    return physPDT;
  }
  return virtPDT;
}

uint64_t * MapCreator::VisiblePDPT() {
  return (uint64_t *)pdpt;
}

MapCreator::MapCreator(PhysRegionList * r, AllocatorList * a) {
  regions = r;
  allocators = a;
  virtMapped = 0;
  hasSwitched = false;
}

void MapCreator::Map(uintptr_t total, uintptr_t current) {
  assert(!(total & 0x1fffff));
  assert(!(current & 0x1fffff));

  virtOffset = current;
  physOffset = current;

  InitializeTables();

  while (virtMapped < total) {

    MapNextPage();
    if (virtMapped >= virtOffset + 0x1000 && !hasSwitched) {
      Switch();
    }

  }

  if (!hasSwitched) {
    Switch();
  }

  cout << "mapped a total of " << virtMapped << " bytes" << endl;

  // create our scratch space
  uint64_t * physScratchPDT, * physScratchPT;
  uint64_t * virtScratchPDT;
  AllocatePage(&physScratchPDT, &virtScratchPDT);
  AllocatePage(&physScratchPT, &virtScratchPT);
  virtScratchPDT[0x1ff] = (uint64_t)physScratchPT | 3;
  VisiblePDPT()[0x1ff] = (uint64_t)physScratchPDT | 3;

  // mark space as used
  MemoryRegion region(0, physOffset);
  cout << "calling allocators->Reserve(region)" << endl;
  allocators->Reserve(region);
}

uint64_t * MapCreator::ScratchPageTable() {
  return virtScratchPT;
}

PhysAddr MapCreator::GetPML4() {
  return pml4;
}

PhysAddr MapCreator::GetPDPT() {
  return pdpt;
}

}

<|endoftext|>


<commit_before>/**
 * \file tests.cpp
 * \author Matt Hammerly
 * \brief This file contains int main() which will run tests as they're written
 */
#include <iostream>
#include <cassert>
#include "Library.h"
#include "Track.h"
#include "Playlist.h"
#include "tests.h"

using std::cout; using std::endl;

/// File location of one track
const std::string track1 = "/home/matt/hdd/dev/cpp/musicmanager/test_data/Gregory And The Hawk/The Boats & Birds EP/01 Boats & Birds.mp3";

/// File location of another track
const std::string track2 = "/home/matt/hdd/dev/cpp/musicmanager/test_data/Beirut/The Flying Club Cup/05 la banlieue.m4a";

/// Title of one playlist
const std::string playlist1 = "test1";

/**
 * \brief Main entry point of program, where tests will be run
 */
int main()
{

    Test_Library_Constructor();

    Test_Library_PrepareDatabase();

    Test_Library_DestroyDatabase();

    Test_Library_AddTrack();

    Test_Library_AddPlaylist();

    Test_Playlist_Constructors();

    Test_Playlist_AppendTrack();

    // So I can poke around manually after running tests
    //CLibrary library;
    //library.PrepareDatabase();

    return 0;
}

/**
 * \brief I mean, there's constructor logic, let's test it.
 */
void Test_Library_Constructor()
{
    cout << "Test_Library_Constructor... ";
    CLibrary library;
    assert(library.GetStatus() == CONNECTION_OK);
    cout << "OK" << endl;
}

/**
 * \brief Ensure all expected database objects are created
 */
void Test_Library_PrepareDatabase()
{
    cout << "Test_Library_PrepareDatabase... ";
    CLibrary library;

    library.PrepareDatabase();

    // We need the unwrapped connection object for arbitrary queries to test
    PGconn *conn = library.GetConnection();

    // Check to see if tables exist
    // Three rows should be returned; one for tracks, one for playlists, one for tracks_playlists
    PGresult* res_tables = PQexec(conn, "SELECT table_name FROM information_schema.tables WHERE table_name IN ('tracks', 'playlists', 'tracks_playlists');");
    assert(PQntuples(res_tables) == 3);
    PQclear(res_tables);

    // Check to see if the tracks_playlists_insert_func procedure exists
    PGresult* res_func = PQexec(conn, "SELECT routine_name FROM information_schema.routines WHERE routine_name = 'tracks_playlists_insert_func';");
    assert(PQntuples(res_func) == 1);
    PQclear(res_func);

    // Check to see if the tracks_playlists_insert_trg trigger exists
    // Two records should be returned; one for ON INSERT, one for ON DELETE
    PGresult* res_trg = PQexec(conn, "SELECT trigger_name FROM information_schema.triggers WHERE trigger_name = 'tracks_playlists_insert_trg';");
    assert(PQntuples(res_trg) == 2);
    PQclear(res_trg);

    // Check to see if the default library playlist was properly created
    PGresult *res_playlist = PQexec(conn, "SELECT id, title, length FROM playlists WHERE id=1");
    assert(PQntuples(res_playlist) == 1);
    PQclear(res_playlist);

    // clean up, I guess
    library.DestroyDatabase();

    cout << "OK" << endl;
}

/**
 * \brief Ensure database objects are deleted when asked
 */
void Test_Library_DestroyDatabase()
{
    cout << "Test_Library_DestroyDatabase... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    library.DestroyDatabase();

    // We need the unwrapped connection object for arbitrary queries to test
    PGconn *conn = library.GetConnection();

    // Check to see if tables exist
    PGresult* res_tables = PQexec(conn, "SELECT table_name FROM information_schema.tables WHERE table_name IN ('tracks', 'playlists', 'tracks_playlists');");
    assert(PQntuples(res_tables) == 0);
    PQclear(res_tables);

    // Check to see if the tracks_playlists_insert_func procedure exists
    PGresult* res_func = PQexec(conn, "SELECT routine_name FROM information_schema.routines WHERE routine_name = 'tracks_playlists_insert_func';");
    assert(PQntuples(res_func) == 0);
    PQclear(res_func);

    // Check to see if the tracks_playlists_insert_trg trigger exists
    PGresult* res_trg = PQexec(conn, "SELECT trigger_name FROM information_schema.triggers WHERE trigger_name = 'tracks_playlists_insert_trg';");
    assert(PQntuples(res_trg) == 0);
    PQclear(res_trg);

    cout << "OK" << endl;

}

/**
 * \brief Ensure tracks can be added properly
 *
 * Maybe later I'll figure out how to verify that the date is correct
 */
void Test_Library_AddTrack()
{
    cout << "Test_Library_AddTrack... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    std::string id = library.AddTrack(track1);

    // We need the unwrapped connection object for arbitrary queries to test
    PGconn* conn = library.GetConnection();

    std::string query = "SELECT * FROM tracks WHERE id=";

    // id should never not be a numeric string but safety first
    char escaped_id[30];
    PQescapeStringConn(conn, escaped_id, id.c_str(), 30, 0);
    query.append(escaped_id);

    PGresult* res = PQexec(conn, query.c_str());

    // Is there actually a row inserted with our id?
    std::string new_id(PQgetvalue(res, 0, 0));
    assert(new_id == id);

    // Is it the same one (same filepath) that we entered?
    std::string filepath(PQgetvalue(res, 0, 1));
    assert(filepath == track1);

    PQclear(res);

    library.DestroyDatabase();

    cout << "OK" << endl;
}

/**
 * \brief Ensure playlists can be added properly
 */
void Test_Library_AddPlaylist()
{
    cout << "Test_Library_AddPlaylist... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    std::string id = library.AddPlaylist(playlist1);

    // We need the unwrapped connection object for arbitrary queries to test
    PGconn *conn = library.GetConnection();

    std::string query = "SELECT * FROM playlists WHERE id=";

    // id should never not be a numeric string but safety first
    char escaped_id[30];
    PQescapeStringConn(conn, escaped_id, id.c_str(), 30, 0);
    query.append(escaped_id);

    PGresult *res = PQexec(conn, query.c_str());

    // Is there actually a row inserted with our id?
    std::string new_id(PQgetvalue(res, 0, 0));
    assert(new_id == id);

    // Is it the same one (same title) that we entered?
    std::string title(PQgetvalue(res, 0, 1));
    assert(title == playlist1);

    PQclear(res);

    library.DestroyDatabase();

    cout << "OK" << endl;
}

/**
 * \brief Ensure playlist objects are constructed properly
 *
 * Note: This isn't done. Verify that non-empty playlists are properly
 * populated with Track objects when the Track class is implemented
 */
void Test_Playlist_Constructors()
{
    cout << "Test_Playlist_Constructors... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    // Should create an empty playlist
    CPlaylist playlist(&library);
    assert(playlist.GetId() == "temp");
    assert(playlist.GetTitle() == "working playlist");
    assert(playlist.GetLength() == "0");
    assert(playlist.GetLibrary() == &library);

    // Should create an object for the default playlist in db
    CPlaylist playlist2(&library, "1");
    assert(playlist2.GetId() == "1");
    assert(playlist2.GetTitle() == "library");
    assert(playlist2.GetLength() == "0");
    assert(playlist2.GetLibrary() == &library);

    library.DestroyDatabase();

    cout << "OK" << endl;
}

/**
 * \brief Ensure tracks can be properly appended to playlists
 *
 * Note: This isn't done. Verify that Track objects are properly appended
 * to their containers when the Track Class is implemented
 */
void Test_Playlist_AppendTrack()
{
    cout << "Test_Playlist_AppendTrack... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    // Adding tracks that can be added to playlists
    std::string track1_id = library.AddTrack(track1);
    std::string track2_id = library.AddTrack(track2);

    CPlaylist db_playlist(&library, "1"); // a playlist from the database
    CPlaylist temp_playlist(&library);    // a temp playlist not in the database

    std::string association1 = db_playlist.AppendTrack(track1_id);
    std::string association2 = db_playlist.AppendTrack(track2_id);

    std::string association3 = temp_playlist.AppendTrack(track1_id);
    std::string association4 = temp_playlist.AppendTrack(track2_id);

    assert(association3 == "temp");
    assert(association4 == "temp");

    // Insert logic to make sure Track objects were appropriately added to the container
    // for both playlists

    // Verify that the proper database records were created
    if (db_playlist.GetId() != "temp")
    {
        PGconn *conn = library.GetConnection();

        std::string query = "SELECT id, playlist_id, track_id, position FROM tracks_playlists WHERE playlist_id = ";

        char escaped_playlist_id[30];
        PQescapeStringConn(conn, escaped_playlist_id, db_playlist.GetId().c_str(), 30, 0);

        query.append(escaped_playlist_id);

        PGresult *res = PQexec(conn, query.c_str());

        std::string association1_id(PQgetvalue(res, 0, 0));
        std::string association1_playlist_id(PQgetvalue(res, 0, 1));
        std::string association1_track_id(PQgetvalue(res, 0, 2));
        std::string association1_position(PQgetvalue(res, 0, 3));
        std::string association2_id(PQgetvalue(res, 1, 0));
        std::string association2_playlist_id(PQgetvalue(res, 1, 1));
        std::string association2_track_id(PQgetvalue(res, 1, 2));
        std::string association2_position(PQgetvalue(res, 1, 3));

        assert(association1 == association1_id);
        assert(db_playlist.GetId() == association1_playlist_id);
        assert(track1_id == association1_track_id);
        assert("1" == association1_position);
        assert(association2 == association2_id);
        assert(db_playlist.GetId() == association2_playlist_id);
        assert(track2_id == association2_track_id);
        assert("2" == association2_position);

        PQclear(res);
    }
    else
    {
        assert(association1 == "temp");
        assert(association2 == "temp");
    }

    library.DestroyDatabase();

    cout << "OK" << endl;
}
<commit_msg>added test for track removal<commit_after>/**
 * \file tests.cpp
 * \author Matt Hammerly
 * \brief This file contains int main() which will run tests as they're written
 */
#include <iostream>
#include <cassert>
#include "Library.h"
#include "Track.h"
#include "Playlist.h"
#include "tests.h"

using std::cout; using std::endl;

/// File location of one track
const std::string track1 = "/home/matt/hdd/dev/cpp/musicmanager/test_data/Gregory And The Hawk/The Boats & Birds EP/01 Boats & Birds.mp3";

/// File location of another track
const std::string track2 = "/home/matt/hdd/dev/cpp/musicmanager/test_data/Beirut/The Flying Club Cup/05 la banlieue.m4a";

/// Title of one playlist
const std::string playlist1 = "test1";

/**
 * \brief Main entry point of program, where tests will be run
 */
int main()
{

    Test_Library_Constructor();

    Test_Library_PrepareDatabase();

    Test_Library_DestroyDatabase();

    Test_Library_AddTrack();

    Test_Library_AddPlaylist();

    Test_Library_RemoveTrack();

    Test_Playlist_Constructors();

    Test_Playlist_AppendTrack();

    // So I can poke around manually after running tests
    //CLibrary library;
    //library.PrepareDatabase();

    return 0;
}

/**
 * \brief I mean, there's constructor logic, let's test it.
 */
void Test_Library_Constructor()
{
    cout << "Test_Library_Constructor... ";
    CLibrary library;
    assert(library.GetStatus() == CONNECTION_OK);
    cout << "OK" << endl;
}

/**
 * \brief Ensure all expected database objects are created
 */
void Test_Library_PrepareDatabase()
{
    cout << "Test_Library_PrepareDatabase... ";
    CLibrary library;

    library.PrepareDatabase();

    // We need the unwrapped connection object for arbitrary queries to test
    PGconn *conn = library.GetConnection();

    // Check to see if tables exist
    // Three rows should be returned; one for tracks, one for playlists, one for tracks_playlists
    PGresult* res_tables = PQexec(conn, "SELECT table_name FROM information_schema.tables WHERE table_name IN ('tracks', 'playlists', 'tracks_playlists');");
    assert(PQntuples(res_tables) == 3);
    PQclear(res_tables);

    // Check to see if the tracks_playlists_insert_func procedure exists
    PGresult* res_func = PQexec(conn, "SELECT routine_name FROM information_schema.routines WHERE routine_name = 'tracks_playlists_insert_func';");
    assert(PQntuples(res_func) == 1);
    PQclear(res_func);

    // Check to see if the tracks_playlists_insert_trg trigger exists
    // Two records should be returned; one for ON INSERT, one for ON DELETE
    PGresult* res_trg = PQexec(conn, "SELECT trigger_name FROM information_schema.triggers WHERE trigger_name = 'tracks_playlists_insert_trg';");
    assert(PQntuples(res_trg) == 2);
    PQclear(res_trg);

    // Check to see if the default library playlist was properly created
    PGresult *res_playlist = PQexec(conn, "SELECT id, title, length FROM playlists WHERE id=1");
    assert(PQntuples(res_playlist) == 1);
    PQclear(res_playlist);

    // clean up, I guess
    library.DestroyDatabase();

    cout << "OK" << endl;
}

/**
 * \brief Ensure database objects are deleted when asked
 */
void Test_Library_DestroyDatabase()
{
    cout << "Test_Library_DestroyDatabase... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    library.DestroyDatabase();

    // We need the unwrapped connection object for arbitrary queries to test
    PGconn *conn = library.GetConnection();

    // Check to see if tables exist
    PGresult* res_tables = PQexec(conn, "SELECT table_name FROM information_schema.tables WHERE table_name IN ('tracks', 'playlists', 'tracks_playlists');");
    assert(PQntuples(res_tables) == 0);
    PQclear(res_tables);

    // Check to see if the tracks_playlists_insert_func procedure exists
    PGresult* res_func = PQexec(conn, "SELECT routine_name FROM information_schema.routines WHERE routine_name = 'tracks_playlists_insert_func';");
    assert(PQntuples(res_func) == 0);
    PQclear(res_func);

    // Check to see if the tracks_playlists_insert_trg trigger exists
    PGresult* res_trg = PQexec(conn, "SELECT trigger_name FROM information_schema.triggers WHERE trigger_name = 'tracks_playlists_insert_trg';");
    assert(PQntuples(res_trg) == 0);
    PQclear(res_trg);

    cout << "OK" << endl;

}

/**
 * \brief Ensure tracks can be added properly
 *
 * Maybe later I'll figure out how to verify that the date is correct
 */
void Test_Library_AddTrack()
{
    cout << "Test_Library_AddTrack... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    std::string id = library.AddTrack(track1);

    // We need the unwrapped connection object for arbitrary queries to test
    PGconn* conn = library.GetConnection();

    std::string query = "SELECT * FROM tracks WHERE id=";

    // id should never not be a numeric string but safety first
    char escaped_id[30];
    PQescapeStringConn(conn, escaped_id, id.c_str(), 30, 0);
    query.append(escaped_id);

    PGresult* res = PQexec(conn, query.c_str());

    // Is there actually a row inserted with our id?
    std::string new_id(PQgetvalue(res, 0, 0));
    assert(new_id == id);

    // Is it the same one (same filepath) that we entered?
    std::string filepath(PQgetvalue(res, 0, 1));
    assert(filepath == track1);

    PQclear(res);

    library.DestroyDatabase();

    cout << "OK" << endl;
}

/**
 * \brief Ensure playlists can be added properly
 */
void Test_Library_AddPlaylist()
{
    cout << "Test_Library_AddPlaylist... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    std::string id = library.AddPlaylist(playlist1);

    // We need the unwrapped connection object for arbitrary queries to test
    PGconn *conn = library.GetConnection();

    std::string query = "SELECT * FROM playlists WHERE id=";

    // id should never not be a numeric string but safety first
    char escaped_id[30];
    PQescapeStringConn(conn, escaped_id, id.c_str(), 30, 0);
    query.append(escaped_id);

    PGresult *res = PQexec(conn, query.c_str());

    // Is there actually a row inserted with our id?
    std::string new_id(PQgetvalue(res, 0, 0));
    assert(new_id == id);

    // Is it the same one (same title) that we entered?
    std::string title(PQgetvalue(res, 0, 1));
    assert(title == playlist1);

    PQclear(res);

    library.DestroyDatabase();

    cout << "OK" << endl;
}

/**
 * \brief Ensure tracks are removed from the database properly
 * This includes removal from all playlists they have been added to
 */
void Test_Library_RemoveTrack()
{
    cout << "Test_Library_RemoveTrack... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    std::string track1_id = library.AddTrack(track1);
    std::string track2_id = library.AddTrack(track2);

    std::string playlist_id = library.AddPlaylist("test");
    CPlaylist playlist(&library, playlist_id);
    playlist.AppendTrack(track1_id);
    playlist.AppendTrack(track2_id);

    PGconn *conn = library.GetConnection();

    std::string query = "SELECT * FROM tracks_playlists WHERE track_id = ";
    char escaped_track1_id[30];
    char escaped_track2_id[30];
    PQescapeStringConn(conn, escaped_track1_id, track1_id.c_str(), 30, 0);
    PQescapeStringConn(conn, escaped_track2_id, track2_id.c_str(), 30, 0);

    std::string query1 = query + escaped_track1_id;
    std::string query2 = query + escaped_track2_id;
    PGresult *res1 = PQexec(conn, query1.c_str());
    PGresult *res2 = PQexec(conn, query2.c_str());

    assert(PQntuples(res1) == 2);
    assert(PQntuples(res2) == 2);

    PQclear(res1);
    PQclear(res2);

    library.RemoveTrack(track1_id);
    library.RemoveTrack(track2_id);

    std::string query3 = "SELECT * FROM tracks WHERE id = ";
    query3.append(escaped_track1_id);
    std::string query4 = "SELECT * FROM tracks WHERE id = ";
    query3.append(escaped_track2_id);
    PGresult *res3 = PQexec(conn, query3.c_str());
    PGresult *res4 = PQexec(conn, query4.c_str());

    res1 = PQexec(conn, query1.c_str());
    res2 = PQexec(conn, query2.c_str());

    assert(PQntuples(res1) == 0);
    assert(PQntuples(res2) == 0);
    assert(PQntuples(res3) == 0);
    assert(PQntuples(res4) == 0);

    PQclear(res1);
    PQclear(res2);
    PQclear(res3);
    PQclear(res4);

    library.DestroyDatabase();

    cout << "OK" << endl;
}

/**
 * \brief Ensure playlist objects are constructed properly
 *
 * Note: This isn't done. Verify that non-empty playlists are properly
 * populated with Track objects when the Track class is implemented
 */
void Test_Playlist_Constructors()
{
    cout << "Test_Playlist_Constructors... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    // Should create an empty playlist
    CPlaylist playlist(&library);
    assert(playlist.GetId() == "temp");
    assert(playlist.GetTitle() == "working playlist");
    assert(playlist.GetLength() == "0");
    assert(playlist.GetLibrary() == &library);

    // Should create an object for the default playlist in db
    CPlaylist playlist2(&library, "1");
    assert(playlist2.GetId() == "1");
    assert(playlist2.GetTitle() == "library");
    assert(playlist2.GetLength() == "0");
    assert(playlist2.GetLibrary() == &library);

    library.DestroyDatabase();

    cout << "OK" << endl;
}

/**
 * \brief Ensure tracks can be properly appended to playlists
 *
 * Note: This isn't done. Verify that Track objects are properly appended
 * to their containers when the Track Class is implemented
 */
void Test_Playlist_AppendTrack()
{
    cout << "Test_Playlist_AppendTrack... ";
    CLibrary library;

    // Make sure all tables and such exist
    library.PrepareDatabase();

    // Adding tracks that can be added to playlists
    std::string track1_id = library.AddTrack(track1);
    std::string track2_id = library.AddTrack(track2);

    CPlaylist db_playlist(&library, "1"); // a playlist from the database
    CPlaylist temp_playlist(&library);    // a temp playlist not in the database

    std::string association1 = db_playlist.AppendTrack(track1_id);
    std::string association2 = db_playlist.AppendTrack(track2_id);

    std::string association3 = temp_playlist.AppendTrack(track1_id);
    std::string association4 = temp_playlist.AppendTrack(track2_id);

    assert(association3 == "temp");
    assert(association4 == "temp");

    // Insert logic to make sure Track objects were appropriately added to the container
    // for both playlists

    // Verify that the proper database records were created
    if (db_playlist.GetId() != "temp")
    {
        PGconn *conn = library.GetConnection();

        std::string query = "SELECT id, playlist_id, track_id, position FROM tracks_playlists WHERE playlist_id = ";

        char escaped_playlist_id[30];
        PQescapeStringConn(conn, escaped_playlist_id, db_playlist.GetId().c_str(), 30, 0);

        query.append(escaped_playlist_id);

        PGresult *res = PQexec(conn, query.c_str());

        std::string association1_id(PQgetvalue(res, 0, 0));
        std::string association1_playlist_id(PQgetvalue(res, 0, 1));
        std::string association1_track_id(PQgetvalue(res, 0, 2));
        std::string association1_position(PQgetvalue(res, 0, 3));
        std::string association2_id(PQgetvalue(res, 1, 0));
        std::string association2_playlist_id(PQgetvalue(res, 1, 1));
        std::string association2_track_id(PQgetvalue(res, 1, 2));
        std::string association2_position(PQgetvalue(res, 1, 3));

        assert(association1 == association1_id);
        assert(db_playlist.GetId() == association1_playlist_id);
        assert(track1_id == association1_track_id);
        assert("1" == association1_position);
        assert(association2 == association2_id);
        assert(db_playlist.GetId() == association2_playlist_id);
        assert(track2_id == association2_track_id);
        assert("2" == association2_position);

        PQclear(res);
    }
    else
    {
        assert(association1 == "temp");
        assert(association2 == "temp");
    }

    library.DestroyDatabase();

    cout << "OK" << endl;
}
<|endoftext|>


<commit_before>/*
* this file is part of the oxygen gtk engine
* Copyright (c) 2010 Hugo Pereira Da Costa <hugo@oxygen-icons.org>
*
* This  library is free  software; you can  redistribute it and/or
* modify it  under  the terms  of the  GNU Lesser  General  Public
* License  as published  by the Free  Software  Foundation; either
* version 2 of the License, or(at your option ) any later version.
*
* This library is distributed  in the hope that it will be useful,
* but  WITHOUT ANY WARRANTY; without even  the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License  along  with  this library;  if not,  write to  the Free
* Software Foundation, Inc., 51  Franklin St, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/

#include "oxygencomboboxdata.h"
#include "../oxygengtkutils.h"

#include <gtk/gtk.h>
#include <iostream>
#include <cassert>

namespace Oxygen
{

    //________________________________________________________________________________
    void ComboBoxData::connect( GtkWidget* widget )
    {
        _enterId = g_signal_connect( G_OBJECT(widget), "enter-notify-event", (GCallback)enterNotifyEvent, this );
        _leaveId = g_signal_connect( G_OBJECT(widget), "leave-notify-event", (GCallback)leaveNotifyEvent, this );
    }

    //________________________________________________________________________________
    void ComboBoxData::disconnect( GtkWidget* widget )
    {

        g_signal_handler_disconnect(G_OBJECT(widget), _enterId );
        g_signal_handler_disconnect(G_OBJECT(widget), _leaveId );
        _entry.disconnect();
        _button.disconnect();

    }

    //________________________________________________________________________________
    void ComboBoxData::setButton( GtkWidget* widget )
    {
        if( _button._widget == widget ) return;
        assert( !_button._widget );

        _button._destroyId = g_signal_connect( G_OBJECT(widget), "destroy", G_CALLBACK( childDestroyNotifyEvent ), this );
        _button._styleChangeId = g_signal_connect( G_OBJECT(widget), "style-set", G_CALLBACK( childStyleChangeNotifyEvent ), this );
        _button._enterId = g_signal_connect( G_OBJECT(widget), "enter-notify-event", (GCallback)enterNotifyEvent, this );
        _button._leaveId = g_signal_connect( G_OBJECT(widget), "leave-notify-event", (GCallback)leaveNotifyEvent, this );
        _button._widget = widget;
    }

    //________________________________________________________________________________
    void ComboBoxData::setEntry( GtkWidget* widget )
    {
        if( _entry._widget == widget ) return;
        assert( !_entry._widget );
        _entry._destroyId = g_signal_connect( G_OBJECT(widget), "destroy", G_CALLBACK( childDestroyNotifyEvent ), this );
        _entry._styleChangeId = g_signal_connect( G_OBJECT(widget), "style-set", G_CALLBACK( childStyleChangeNotifyEvent ), this );
        _entry._enterId = g_signal_connect( G_OBJECT(widget), "enter-notify-event", (GCallback)enterNotifyEvent, this );
        _entry._leaveId = g_signal_connect( G_OBJECT(widget), "leave-notify-event", (GCallback)leaveNotifyEvent, this );
        _entry._widget = widget;
    }

    //________________________________________________________________________________
    void ComboBoxData::setHovered( GtkWidget* widget, bool value )
    {
        const bool oldHovered( hovered() );
        if( widget == _entry._widget ) _entry._hovered = value;
        else if( widget == _button._widget ) _button._hovered = value;
        else _hovered = value;

        if( oldHovered != hovered() )
        {
            // trigger repaint
            //std::cout << "ComboBoxData::setHovered - " << hovered() << std::endl;
            if( _button._widget ) gtk_widget_queue_draw( gtk_widget_get_parent( _button._widget ) );
            else if( _entry._widget ) gtk_widget_queue_draw( gtk_widget_get_parent( _entry._widget ) );
        }

    }

    //________________________________________________________________________________
    void ComboBoxData::unregisterChild( GtkWidget* widget )
    {

        if( widget == _button._widget ) _button.disconnect();
        else if( widget == _entry._widget ) _entry.disconnect();
        return;

    }

    //________________________________________________________________________________
    void ComboBoxData::Data::disconnect( void )
    {
        if( !_widget ) return;
        g_signal_handler_disconnect(G_OBJECT(_widget), _destroyId );
        g_signal_handler_disconnect(G_OBJECT(_widget), _styleChangeId );
        g_signal_handler_disconnect(G_OBJECT(_widget), _enterId );
        g_signal_handler_disconnect(G_OBJECT(_widget), _leaveId );
        _widget = 0L;
    }

    //____________________________________________________________________________________________
    gboolean ComboBoxData::childDestroyNotifyEvent( GtkWidget* widget, gpointer data )
    {
        static_cast<ComboBoxData*>(data)->unregisterChild( widget );
        return FALSE;
    }

    //____________________________________________________________________________________________
    void ComboBoxData::childStyleChangeNotifyEvent( GtkWidget* widget, GtkStyle*, gpointer data )
    { static_cast<ComboBoxData*>(data)->unregisterChild( widget ); }

    //________________________________________________________________________________
    gboolean ComboBoxData::enterNotifyEvent( GtkWidget* widget, GdkEventCrossing*, gpointer data )
    {
        // simply triggers repaint
        static_cast<ComboBoxData*>( data )->setHovered( widget, true );
        return FALSE;
    }

    //________________________________________________________________________________
    gboolean ComboBoxData::leaveNotifyEvent( GtkWidget* widget, GdkEventCrossing*, gpointer data )
    {
        // simply triggers repaint
        static_cast<ComboBoxData*>( data )->setHovered( widget, false );
        return FALSE;
    }

}
<commit_msg>removed obsolete comments.<commit_after>/*
* this file is part of the oxygen gtk engine
* Copyright (c) 2010 Hugo Pereira Da Costa <hugo@oxygen-icons.org>
*
* This  library is free  software; you can  redistribute it and/or
* modify it  under  the terms  of the  GNU Lesser  General  Public
* License  as published  by the Free  Software  Foundation; either
* version 2 of the License, or(at your option ) any later version.
*
* This library is distributed  in the hope that it will be useful,
* but  WITHOUT ANY WARRANTY; without even  the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License  along  with  this library;  if not,  write to  the Free
* Software Foundation, Inc., 51  Franklin St, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/

#include "oxygencomboboxdata.h"
#include "../oxygengtkutils.h"

#include <gtk/gtk.h>
#include <iostream>
#include <cassert>

namespace Oxygen
{

    //________________________________________________________________________________
    void ComboBoxData::connect( GtkWidget* widget )
    {
        _enterId = g_signal_connect( G_OBJECT(widget), "enter-notify-event", (GCallback)enterNotifyEvent, this );
        _leaveId = g_signal_connect( G_OBJECT(widget), "leave-notify-event", (GCallback)leaveNotifyEvent, this );
    }

    //________________________________________________________________________________
    void ComboBoxData::disconnect( GtkWidget* widget )
    {

        g_signal_handler_disconnect(G_OBJECT(widget), _enterId );
        g_signal_handler_disconnect(G_OBJECT(widget), _leaveId );
        _entry.disconnect();
        _button.disconnect();

    }

    //________________________________________________________________________________
    void ComboBoxData::setButton( GtkWidget* widget )
    {
        if( _button._widget == widget ) return;
        assert( !_button._widget );

        _button._destroyId = g_signal_connect( G_OBJECT(widget), "destroy", G_CALLBACK( childDestroyNotifyEvent ), this );
        _button._styleChangeId = g_signal_connect( G_OBJECT(widget), "style-set", G_CALLBACK( childStyleChangeNotifyEvent ), this );
        _button._enterId = g_signal_connect( G_OBJECT(widget), "enter-notify-event", (GCallback)enterNotifyEvent, this );
        _button._leaveId = g_signal_connect( G_OBJECT(widget), "leave-notify-event", (GCallback)leaveNotifyEvent, this );
        _button._widget = widget;
    }

    //________________________________________________________________________________
    void ComboBoxData::setEntry( GtkWidget* widget )
    {
        if( _entry._widget == widget ) return;
        assert( !_entry._widget );
        _entry._destroyId = g_signal_connect( G_OBJECT(widget), "destroy", G_CALLBACK( childDestroyNotifyEvent ), this );
        _entry._styleChangeId = g_signal_connect( G_OBJECT(widget), "style-set", G_CALLBACK( childStyleChangeNotifyEvent ), this );
        _entry._enterId = g_signal_connect( G_OBJECT(widget), "enter-notify-event", (GCallback)enterNotifyEvent, this );
        _entry._leaveId = g_signal_connect( G_OBJECT(widget), "leave-notify-event", (GCallback)leaveNotifyEvent, this );
        _entry._widget = widget;
    }

    //________________________________________________________________________________
    void ComboBoxData::setHovered( GtkWidget* widget, bool value )
    {
        const bool oldHovered( hovered() );
        if( widget == _entry._widget ) _entry._hovered = value;
        else if( widget == _button._widget ) _button._hovered = value;
        else _hovered = value;

        if( oldHovered != hovered() )
        {
            // trigger repaint
            if( _button._widget ) gtk_widget_queue_draw( gtk_widget_get_parent( _button._widget ) );
            else if( _entry._widget ) gtk_widget_queue_draw( gtk_widget_get_parent( _entry._widget ) );
        }

    }

    //________________________________________________________________________________
    void ComboBoxData::unregisterChild( GtkWidget* widget )
    {

        if( widget == _button._widget ) _button.disconnect();
        else if( widget == _entry._widget ) _entry.disconnect();
        return;

    }

    //________________________________________________________________________________
    void ComboBoxData::Data::disconnect( void )
    {
        if( !_widget ) return;
        g_signal_handler_disconnect(G_OBJECT(_widget), _destroyId );
        g_signal_handler_disconnect(G_OBJECT(_widget), _styleChangeId );
        g_signal_handler_disconnect(G_OBJECT(_widget), _enterId );
        g_signal_handler_disconnect(G_OBJECT(_widget), _leaveId );
        _widget = 0L;
    }

    //____________________________________________________________________________________________
    gboolean ComboBoxData::childDestroyNotifyEvent( GtkWidget* widget, gpointer data )
    {
        static_cast<ComboBoxData*>(data)->unregisterChild( widget );
        return FALSE;
    }

    //____________________________________________________________________________________________
    void ComboBoxData::childStyleChangeNotifyEvent( GtkWidget* widget, GtkStyle*, gpointer data )
    { static_cast<ComboBoxData*>(data)->unregisterChild( widget ); }

    //________________________________________________________________________________
    gboolean ComboBoxData::enterNotifyEvent( GtkWidget* widget, GdkEventCrossing*, gpointer data )
    {
        static_cast<ComboBoxData*>( data )->setHovered( widget, true );
        return FALSE;
    }

    //________________________________________________________________________________
    gboolean ComboBoxData::leaveNotifyEvent( GtkWidget* widget, GdkEventCrossing*, gpointer data )
    {
        static_cast<ComboBoxData*>( data )->setHovered( widget, false );
        return FALSE;
    }

}
<|endoftext|>


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

#include <iostream>

/*
* NAOQI
*/
#include <qi/anyobject.hpp>
#include <alvision/alvisiondefinitions.h> // for kTop...
/*
* BOOST
*/
#include <boost/foreach.hpp>
#include <boost/make_shared.hpp>
#define foreach BOOST_FOREACH
/*
* ROS
*/
#include <std_msgs/Int32.h>

/*
* PUBLIC INTERFACE
*/
#include <alrosbridge/alrosbridge.hpp>

/*
* publishers
*/
#include "publishers/camera.hpp"
#include "publishers/diagnostics.hpp"
#include "publishers/int.hpp"
#include "publishers/info.hpp"
#include "publishers/joint_state.hpp"
#include "publishers/nao_joint_state.hpp"
#include "publishers/odometry.hpp"
#include "publishers/laser.hpp"
#include "publishers/log.hpp"
#include "publishers/sonar.hpp"
#include "publishers/string.hpp"

/*
 * subscribers
 */
#include "subscribers/teleop.hpp"
#include "subscribers/moveto.hpp"

/*
* STATIC FUNCTIONS INCLUDE
*/
#include "ros_env.hpp"
#include "helpers.hpp"

/*
 * ROS
 */
#include <tf2_ros/buffer.h>

namespace alros
{

Bridge::Bridge( qi::SessionPtr& session )
  : sessionPtr_( session ),
  freq_(15),
  publish_enabled_(false),
  publish_cancelled_(false)
{
  std::cout << "application path "<< std::endl;
}

Bridge::~Bridge()
{
  std::cout << "ALRosBridge is shutting down.." << std::endl;
  publish_cancelled_ = true;
  stop();
  if (publisherThread_.get_id() !=  boost::thread::id())
    publisherThread_.join();
  // destroy nodehandle?
  if(nhPtr_)
  {
    nhPtr_->shutdown();
    ros::shutdown();
  }
}

void Bridge::stopService() {
  publishers_.clear();
  subscribers_.clear();
}

void Bridge::rosLoop()
{
  while( !publish_cancelled_ )
  {
   {
      boost::mutex::scoped_lock lock( mutex_reinit_ );
      if (publish_enabled_ && !pub_queue_.empty())
      {
        // Wait for the next Publisher to be ready
        size_t pub_index = pub_queue_.top().pub_index_;
        publisher::Publisher& pub = publishers_[pub_index];
        ros::Time schedule = pub_queue_.top().schedule_;

        ros::Duration(schedule - ros::Time::now()).sleep();

        if ( pub.isSubscribed() && pub.isInitialized())
        {
          pub.publish();
        }

        // Schedule for a future time or not
        pub_queue_.pop();
        if ( pub.frequency() != 0 )
          pub_queue_.push(ScheduledPublish(schedule + ros::Duration(1.0f / pub.frequency()), pub_index));
      } else
        // sleep one second
        ros::Duration(1).sleep();
    }
    ros::spinOnce();
  }
}

// public interface here
void Bridge::registerPublisher( publisher::Publisher pub )
{
  std::vector<publisher::Publisher>::iterator it;
  it = std::find( publishers_.begin(), publishers_.end(), pub );

  // if publisher is not found, register it!
  if (it == publishers_.end() )
  {
    publishers_.push_back( pub );
    it = publishers_.end() - 1;
    std::cout << "registered publisher:\t" << pub.name() << std::endl;
  }
  // if found, re-init them
  else
  {
    std::cout << "re-initialized existing publisher:\t" << it->name() << std::endl;
  }
}

void Bridge::registerDefaultPublisher()
{
  if (!publishers_.empty())
    return;

  // Info should be at 0 (latched) but somehow that does not work ...
  publisher::Publisher info = alros::publisher::InfoPublisher("info", "info", 0.1, sessionPtr_);
  registerPublisher( info );

  // Define the tf2 buffer
  if (info.robot() == alros::PEPPER)
  {
    alros::publisher::JointStatePublisher publisher("joint_states", "/joint_states", 15, sessionPtr_);
    tf2_buffer_ = publisher.getTF2Buffer();
    registerPublisher( publisher );
  }
  if (info.robot() == alros::NAO)
  {
    alros::publisher::NaoJointStatePublisher publisher( "nao_joint_states", "/joint_states", 15, sessionPtr_);
    tf2_buffer_ = publisher.getTF2Buffer();
    registerPublisher( publisher );
  }

  registerPublisher( alros::publisher::OdometryPublisher( "odometry", "/odom", 15, sessionPtr_, tf2_buffer_) );
  registerPublisher( alros::publisher::CameraPublisher("front_camera", "camera/front", 10, sessionPtr_, AL::kTopCamera, AL::kQVGA) );
  registerPublisher( alros::publisher::DiagnosticsPublisher("diagnostics", 1, sessionPtr_) );
  registerPublisher( alros::publisher::SonarPublisher("sonar", "sonar", 10, sessionPtr_) );
  registerPublisher( alros::publisher::LogPublisher("logger", "", 5, sessionPtr_) );

  // Pepper specific publishers
  if (info.robot() == alros::PEPPER)
  {
    registerPublisher( alros::publisher::LaserPublisher("laser", "laser", 10, sessionPtr_) );
    registerPublisher( alros::publisher::CameraPublisher("depth_camera", "camera/depth", 10, sessionPtr_, AL::kDepthCamera, AL::kQVGA) );
  }
}

// public interface here
void Bridge::registerSubscriber( subscriber::Subscriber sub )
{
  std::vector<subscriber::Subscriber>::iterator it;
  it = std::find( subscribers_.begin(), subscribers_.end(), sub );
  size_t sub_index = 0;

  // if subscriber is not found, register it!
  if (it == subscribers_.end() )
  {
    sub_index = subscribers_.size();
    subscribers_.push_back( sub );
    std::cout << "registered subscriber:\t" << sub.name() << std::endl;
  }
  // if found, re-init them
  else
  {
    std::cout << "re-initialized existing subscriber:\t" << it->name() << std::endl;
  }
}

void Bridge::registerDefaultSubscriber()
{
  if (!subscribers_.empty())
    return;
  registerSubscriber( alros::subscriber::TeleopSubscriber("teleop", "/cmd_vel", sessionPtr_) );
  registerSubscriber( alros::subscriber::MovetoSubscriber("moveto", "/move_base_simple/goal", sessionPtr_, tf2_buffer_) );
}

void Bridge::init()
{
  pub_queue_ =  std::priority_queue<ScheduledPublish>();
  size_t pub_index = 0;
  foreach( publisher::Publisher& pub, publishers_ )
  {
    pub.reset( *nhPtr_ );

    // Schedule it for the next publish
    pub_queue_.push(ScheduledPublish(ros::Time::now(), pub_index));
    ++pub_index;
  }
  foreach( subscriber::Subscriber& sub, subscribers_ )
  {
    sub.reset( *nhPtr_ );
  }
}

/*
* EXPOSED FUNCTIONS
*/

std::string Bridge::getMasterURI() const
{
  return ros_env::getMasterURI();
}

void Bridge::setMasterURI( const std::string& uri)
{
  setMasterURINet(uri, "eth0");
}
void Bridge::setMasterURINet( const std::string& uri, const std::string& network_interface)
{
  // Stopping publishing
  stop();

  // Reinitializing ROS
  {
    boost::mutex::scoped_lock lock( mutex_reinit_ );
    nhPtr_.reset();
    std::cout << "nodehandle reset " << std::endl;
    ros_env::setMasterURI( uri, network_interface );
    nhPtr_.reset( new ros::NodeHandle("~") );
  }
  // Create the publishing thread if needed
  if (publisherThread_.get_id() ==  boost::thread::id())
    publisherThread_ = boost::thread( &Bridge::rosLoop, this );

  // register publishers, that will not start them
  registerDefaultPublisher();
  registerDefaultSubscriber();
  // initialize the publishers and subscribers with nodehandle
  init();
  // Start publishing again
  start();
}

void Bridge::start()
{
  boost::mutex::scoped_lock lock( mutex_reinit_ );
  publish_enabled_ = true;
}

void Bridge::stop()
{
  boost::mutex::scoped_lock lock( mutex_reinit_ );
  publish_enabled_ = false;
}

QI_REGISTER_OBJECT( Bridge, start, stop, getMasterURI, setMasterURI, setMasterURINet );
} //alros
<commit_msg>Stop publishing thread before removing the publishers and subscribers<commit_after>/*
 * Copyright 2015 Aldebaran
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

#include <iostream>

/*
* NAOQI
*/
#include <qi/anyobject.hpp>
#include <alvision/alvisiondefinitions.h> // for kTop...
/*
* BOOST
*/
#include <boost/foreach.hpp>
#include <boost/make_shared.hpp>
#define foreach BOOST_FOREACH
/*
* ROS
*/
#include <std_msgs/Int32.h>

/*
* PUBLIC INTERFACE
*/
#include <alrosbridge/alrosbridge.hpp>

/*
* publishers
*/
#include "publishers/camera.hpp"
#include "publishers/diagnostics.hpp"
#include "publishers/int.hpp"
#include "publishers/info.hpp"
#include "publishers/joint_state.hpp"
#include "publishers/nao_joint_state.hpp"
#include "publishers/odometry.hpp"
#include "publishers/laser.hpp"
#include "publishers/log.hpp"
#include "publishers/sonar.hpp"
#include "publishers/string.hpp"

/*
 * subscribers
 */
#include "subscribers/teleop.hpp"
#include "subscribers/moveto.hpp"

/*
* STATIC FUNCTIONS INCLUDE
*/
#include "ros_env.hpp"
#include "helpers.hpp"

/*
 * ROS
 */
#include <tf2_ros/buffer.h>

namespace alros
{

Bridge::Bridge( qi::SessionPtr& session )
  : sessionPtr_( session ),
  freq_(15),
  publish_enabled_(false),
  publish_cancelled_(false)
{
  std::cout << "application path "<< std::endl;
}

Bridge::~Bridge()
{
  std::cout << "ALRosBridge is shutting down.." << std::endl;
  // destroy nodehandle?
  if(nhPtr_)
  {
    nhPtr_->shutdown();
    ros::shutdown();
  }
}

void Bridge::stopService() {
  publish_cancelled_ = true;
  stop();
  if (publisherThread_.get_id() !=  boost::thread::id())
    publisherThread_.join();
  publishers_.clear();
  subscribers_.clear();
}

void Bridge::rosLoop()
{
  while( !publish_cancelled_ )
  {
   {
      boost::mutex::scoped_lock lock( mutex_reinit_ );
      if (publish_enabled_ && !pub_queue_.empty())
      {
        // Wait for the next Publisher to be ready
        size_t pub_index = pub_queue_.top().pub_index_;
        publisher::Publisher& pub = publishers_[pub_index];
        ros::Time schedule = pub_queue_.top().schedule_;

        ros::Duration(schedule - ros::Time::now()).sleep();

        if ( pub.isSubscribed() && pub.isInitialized())
        {
          pub.publish();
        }

        // Schedule for a future time or not
        pub_queue_.pop();
        if ( pub.frequency() != 0 )
          pub_queue_.push(ScheduledPublish(schedule + ros::Duration(1.0f / pub.frequency()), pub_index));
      } else
        // sleep one second
        ros::Duration(1).sleep();
    }
    ros::spinOnce();
  }
}

// public interface here
void Bridge::registerPublisher( publisher::Publisher pub )
{
  std::vector<publisher::Publisher>::iterator it;
  it = std::find( publishers_.begin(), publishers_.end(), pub );

  // if publisher is not found, register it!
  if (it == publishers_.end() )
  {
    publishers_.push_back( pub );
    it = publishers_.end() - 1;
    std::cout << "registered publisher:\t" << pub.name() << std::endl;
  }
  // if found, re-init them
  else
  {
    std::cout << "re-initialized existing publisher:\t" << it->name() << std::endl;
  }
}

void Bridge::registerDefaultPublisher()
{
  if (!publishers_.empty())
    return;

  // Info should be at 0 (latched) but somehow that does not work ...
  publisher::Publisher info = alros::publisher::InfoPublisher("info", "info", 0.1, sessionPtr_);
  registerPublisher( info );

  // Define the tf2 buffer
  if (info.robot() == alros::PEPPER)
  {
    alros::publisher::JointStatePublisher publisher("joint_states", "/joint_states", 15, sessionPtr_);
    tf2_buffer_ = publisher.getTF2Buffer();
    registerPublisher( publisher );
  }
  if (info.robot() == alros::NAO)
  {
    alros::publisher::NaoJointStatePublisher publisher( "nao_joint_states", "/joint_states", 15, sessionPtr_);
    tf2_buffer_ = publisher.getTF2Buffer();
    registerPublisher( publisher );
  }

  registerPublisher( alros::publisher::OdometryPublisher( "odometry", "/odom", 15, sessionPtr_, tf2_buffer_) );
  registerPublisher( alros::publisher::CameraPublisher("front_camera", "camera/front", 10, sessionPtr_, AL::kTopCamera, AL::kQVGA) );
  registerPublisher( alros::publisher::DiagnosticsPublisher("diagnostics", 1, sessionPtr_) );
  registerPublisher( alros::publisher::SonarPublisher("sonar", "sonar", 10, sessionPtr_) );
  registerPublisher( alros::publisher::LogPublisher("logger", "", 5, sessionPtr_) );

  // Pepper specific publishers
  if (info.robot() == alros::PEPPER)
  {
    registerPublisher( alros::publisher::LaserPublisher("laser", "laser", 10, sessionPtr_) );
    registerPublisher( alros::publisher::CameraPublisher("depth_camera", "camera/depth", 10, sessionPtr_, AL::kDepthCamera, AL::kQVGA) );
  }
}

// public interface here
void Bridge::registerSubscriber( subscriber::Subscriber sub )
{
  std::vector<subscriber::Subscriber>::iterator it;
  it = std::find( subscribers_.begin(), subscribers_.end(), sub );
  size_t sub_index = 0;

  // if subscriber is not found, register it!
  if (it == subscribers_.end() )
  {
    sub_index = subscribers_.size();
    subscribers_.push_back( sub );
    std::cout << "registered subscriber:\t" << sub.name() << std::endl;
  }
  // if found, re-init them
  else
  {
    std::cout << "re-initialized existing subscriber:\t" << it->name() << std::endl;
  }
}

void Bridge::registerDefaultSubscriber()
{
  if (!subscribers_.empty())
    return;
  registerSubscriber( alros::subscriber::TeleopSubscriber("teleop", "/cmd_vel", sessionPtr_) );
  registerSubscriber( alros::subscriber::MovetoSubscriber("moveto", "/move_base_simple/goal", sessionPtr_, tf2_buffer_) );
}

void Bridge::init()
{
  pub_queue_ =  std::priority_queue<ScheduledPublish>();
  size_t pub_index = 0;
  foreach( publisher::Publisher& pub, publishers_ )
  {
    pub.reset( *nhPtr_ );

    // Schedule it for the next publish
    pub_queue_.push(ScheduledPublish(ros::Time::now(), pub_index));
    ++pub_index;
  }
  foreach( subscriber::Subscriber& sub, subscribers_ )
  {
    sub.reset( *nhPtr_ );
  }
}

/*
* EXPOSED FUNCTIONS
*/

std::string Bridge::getMasterURI() const
{
  return ros_env::getMasterURI();
}

void Bridge::setMasterURI( const std::string& uri)
{
  setMasterURINet(uri, "eth0");
}
void Bridge::setMasterURINet( const std::string& uri, const std::string& network_interface)
{
  // Stopping publishing
  stop();

  // Reinitializing ROS
  {
    boost::mutex::scoped_lock lock( mutex_reinit_ );
    nhPtr_.reset();
    std::cout << "nodehandle reset " << std::endl;
    ros_env::setMasterURI( uri, network_interface );
    nhPtr_.reset( new ros::NodeHandle("~") );
  }
  // Create the publishing thread if needed
  if (publisherThread_.get_id() ==  boost::thread::id())
    publisherThread_ = boost::thread( &Bridge::rosLoop, this );

  // register publishers, that will not start them
  registerDefaultPublisher();
  registerDefaultSubscriber();
  // initialize the publishers and subscribers with nodehandle
  init();
  // Start publishing again
  start();
}

void Bridge::start()
{
  boost::mutex::scoped_lock lock( mutex_reinit_ );
  publish_enabled_ = true;
}

void Bridge::stop()
{
  boost::mutex::scoped_lock lock( mutex_reinit_ );
  publish_enabled_ = false;
}

QI_REGISTER_OBJECT( Bridge, start, stop, getMasterURI, setMasterURI, setMasterURINet );
} //alros
<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Denis Mingulov.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** No Commercial Usage
**
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/

#include "classviewsymbollocation.h"
#include "classviewmanager.h"
#include "classviewnavigationwidgetfactory.h"
#include "classviewparser.h"
#include "classviewutils.h"

#include <utils/qtcassert.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
#include <texteditor/basetexteditor.h>
#include <cplusplus/ModelManagerInterface.h>
#include <cpptools/cpptoolsconstants.h>
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <texteditor/itexteditor.h>

#include <QtCore/QThread>
#include <QtCore/QMutex>
#include <QtCore/QMutexLocker>

namespace ClassView {
namespace Internal {

///////////////////////////////// ManagerPrivate //////////////////////////////////

/*!
   \struct ManagerPrivate
   \brief Private class data for \a Manager
   \sa Manager
 */
struct ManagerPrivate
{
    ManagerPrivate() : state(false) {}

    //! instance
    static Manager *instance;

    //! Pointer to widget factory
    QPointer<NavigationWidgetFactory> widgetFactory;

    //! State mutex
    QMutex mutexState;

    //! Internal manager state. \sa Manager::state
    bool state;

    //! code state/changes parser
    Parser parser;

    //! separate thread for the parser
    QThread parserThread;

    //! there is some massive operation ongoing so temporary we should wait
    bool disableCodeParser;
};

// static variable initialization
Manager *ManagerPrivate::instance = 0;

///////////////////////////////// Manager //////////////////////////////////

Manager::Manager(QObject *parent)
    : QObject(parent),
    d_ptr(new ManagerPrivate())
{
    d_ptr->widgetFactory = NavigationWidgetFactory::instance();

    // register - to be able send between signal/slots
    qRegisterMetaType<QSharedPointer<QStandardItem> >("QSharedPointer<QStandardItem>");

    initialize();

    // start a separate thread for the parser
    d_ptr->parser.moveToThread(&d_ptr->parserThread);
    d_ptr->parserThread.start();

    // initial setup
    onProjectListChanged();
}

Manager::~Manager()
{
    d_ptr->parserThread.quit();
    d_ptr->parserThread.wait();
}

Manager *Manager::instance(QObject *parent)
{
    if (!ManagerPrivate::instance)
        ManagerPrivate::instance = new Manager(parent);
    return ManagerPrivate::instance;
}

bool Manager::canFetchMore(QStandardItem *item) const
{
    return d_ptr->parser.canFetchMore(item);
}

void Manager::fetchMore(QStandardItem *item, bool skipRoot)
{
    d_ptr->parser.fetchMore(item, skipRoot);
}

void Manager::initialize()
{
    // use Qt::QueuedConnection everywhere

    // widget factory signals
    connect(d_ptr->widgetFactory, SIGNAL(widgetIsCreated()),
            SLOT(onWidgetIsCreated()), Qt::QueuedConnection);

    // internal manager state is changed
    connect(this, SIGNAL(stateChanged(bool)), SLOT(onStateChanged(bool)), Qt::QueuedConnection);

    // connections to enable/disable navi widget factory
    ProjectExplorer::SessionManager *sessionManager =
        ProjectExplorer::ProjectExplorerPlugin::instance()->session();
    connect(sessionManager, SIGNAL(projectAdded(ProjectExplorer::Project*)),
            SLOT(onProjectListChanged()), Qt::QueuedConnection);
    connect(sessionManager, SIGNAL(projectRemoved(ProjectExplorer::Project*)),
            SLOT(onProjectListChanged()), Qt::QueuedConnection);

    Core::ICore *core = Core::ICore::instance();

    // connect to the progress manager for signals about Parsing tasks
    connect(core->progressManager(), SIGNAL(taskStarted(QString)),
            SLOT(onTaskStarted(QString)), Qt::QueuedConnection);
    connect(core->progressManager(), SIGNAL(allTasksFinished(QString)),
            SLOT(onAllTasksFinished(QString)), Qt::QueuedConnection);

    // when we signals that really document is updated - sent it to the parser
    connect(this, SIGNAL(requestDocumentUpdated(CPlusPlus::Document::Ptr)),
            &d_ptr->parser, SLOT(parseDocument(CPlusPlus::Document::Ptr)), Qt::QueuedConnection);

    // translate data update from the parser to listeners
    connect(&d_ptr->parser, SIGNAL(treeDataUpdate(QSharedPointer<QStandardItem>)),
            this, SLOT(onTreeDataUpdate(QSharedPointer<QStandardItem>)), Qt::QueuedConnection);

    // requet current state - immediately after a notification
    connect(this, SIGNAL(requestTreeDataUpdate()),
            &d_ptr->parser, SLOT(requestCurrentState()), Qt::QueuedConnection);

    // full reset request to parser
    connect(this, SIGNAL(requestResetCurrentState()),
            &d_ptr->parser, SLOT(resetDataToCurrentState()), Qt::QueuedConnection);

    // clear cache request
    connect(this, SIGNAL(requestClearCache()),
            &d_ptr->parser, SLOT(clearCache()), Qt::QueuedConnection);

    // clear full cache request
    connect(this, SIGNAL(requestClearCacheAll()),
            &d_ptr->parser, SLOT(clearCacheAll()), Qt::QueuedConnection);

    // flat mode request
    connect(this, SIGNAL(requestSetFlatMode(bool)),
            &d_ptr->parser, SLOT(setFlatMode(bool)), Qt::QueuedConnection);

    // connect to the cpp model manager for signals about document updates
    CPlusPlus::CppModelManagerInterface *codeModelManager
        = CPlusPlus::CppModelManagerInterface::instance();

    // when code manager signals that document is updated - handle it by ourselves
    connect(codeModelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
            SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)), Qt::QueuedConnection);
    //
    connect(codeModelManager, SIGNAL(aboutToRemoveFiles(QStringList)),
            &d_ptr->parser, SLOT(removeFiles(QStringList)), Qt::QueuedConnection);
}

bool Manager::state() const
{
    return d_ptr->state;
}

void Manager::setState(bool state)
{
    QMutexLocker locker(&d_ptr->mutexState);

    // boolean comparsion - should be done correctly by any compiler
    if (state == d_ptr->state)
        return;

    d_ptr->state = state;

    emit stateChanged(d_ptr->state);
}

void Manager::onWidgetIsCreated()
{
    // do nothing - continue to sleep
}

void Manager::onWidgetVisibilityIsChanged(bool visibility)
{
    // activate data handling - when 1st time 'Class View' will be open
    if (visibility)
        setState(true);
}

void Manager::onStateChanged(bool state)
{
    if (state) {
        // enabled - request a current snapshots etc?..
        emit requestResetCurrentState();
    } else {
        // disabled - clean parsers internal cache
        emit requestClearCacheAll();
    }
}

void Manager::onProjectListChanged()
{
    // do nothing if Manager is disabled
    if (!state())
        return;

    // update to the latest state
    requestTreeDataUpdate();
}

void Manager::onTaskStarted(const QString &type)
{
    if (type != CppTools::Constants::TASK_INDEX)
        return;

    // disable tree updates to speed up
    d_ptr->disableCodeParser = true;
}

void Manager::onAllTasksFinished(const QString &type)
{
    if (type != CppTools::Constants::TASK_INDEX)
        return;

    // parsing is finished, enable tree updates
    d_ptr->disableCodeParser = false;

    // do nothing if Manager is disabled
    if (!state())
        return;

    // any document might be changed, emit signal to clear cache
    emit requestClearCache();

    // request to update a tree to the current state
    emit requestResetCurrentState();
}

void Manager::onDocumentUpdated(CPlusPlus::Document::Ptr doc)
{
    // do nothing if Manager is disabled
    if (!state())
        return;

    // do nothing if updates are disabled
    if (d_ptr->disableCodeParser)
        return;

    emit requestDocumentUpdated(doc);
}

void Manager::gotoLocation(const QString &fileName, int line, int column)
{
    bool newEditor = false;
    TextEditor::BaseTextEditor::openEditorAt(fileName, line, column, QString(),
                                             Core::EditorManager::IgnoreNavigationHistory,
                                             &newEditor);
}

void Manager::gotoLocations(const QList<QVariant> &list)
{
    QSet<SymbolLocation> locations = Utils::roleToLocations(list);

    if (locations.count() == 0)
        return;

    QString fileName;
    int line = 0;
    int column = 0;
    bool currentPositionAvailable = false;

    // what is open now?
    Core::IEditor *editor = Core::EditorManager::instance()->currentEditor();
    if (editor) {
        // get current file name
        Core::IFile *file = editor->file();
        if (file)
            fileName = file->fileName();

        // if text file - what is current position?
        TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
        if (textEditor) {
            // there is open currently text editor
            int position = textEditor->position();
            textEditor->convertPosition(position, &line, &column);
            currentPositionAvailable = true;
        }
    }

    // if there is something open - try to check, is it currently activated symbol?
    if (currentPositionAvailable) {
        SymbolLocation current(fileName, line, column);
        QSet<SymbolLocation>::const_iterator it = locations.find(current);
        QSet<SymbolLocation>::const_iterator end = locations.constEnd();
        // is it known location?
        if (it != end) {
            // found - do one additional step
            ++it;
            if (it == end)
                it = locations.begin();
            const SymbolLocation &found = *it;
            gotoLocation(found.fileName(), found.line(), found.column());
            return;
        }
    }

    // no success - open first item in the list
    const SymbolLocation loc = *locations.constBegin();

    gotoLocation(loc.fileName(), loc.line(), loc.column());
}

void Manager::onRequestTreeDataUpdate()
{
    // do nothing if Manager is disabled
    if (!state())
        return;

    emit requestTreeDataUpdate();
}

void Manager::setFlatMode(bool flat)
{
    emit requestSetFlatMode(flat);
}

void Manager::onTreeDataUpdate(QSharedPointer<QStandardItem> result)
{
    // do nothing if Manager is disabled
    if (!state())
        return;

    emit treeDataUpdate(result);
}

} // namespace Internal
} // namespace ClassView
<commit_msg>classview: add a member initialization<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Denis Mingulov.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** No Commercial Usage
**
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**************************************************************************/

#include "classviewsymbollocation.h"
#include "classviewmanager.h"
#include "classviewnavigationwidgetfactory.h"
#include "classviewparser.h"
#include "classviewutils.h"

#include <utils/qtcassert.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
#include <texteditor/basetexteditor.h>
#include <cplusplus/ModelManagerInterface.h>
#include <cpptools/cpptoolsconstants.h>
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <texteditor/itexteditor.h>

#include <QtCore/QThread>
#include <QtCore/QMutex>
#include <QtCore/QMutexLocker>

namespace ClassView {
namespace Internal {

///////////////////////////////// ManagerPrivate //////////////////////////////////

/*!
   \struct ManagerPrivate
   \internal
   \brief Private class data for \a Manager
   \sa Manager
 */
struct ManagerPrivate
{
    ManagerPrivate() : state(false), disableCodeParser(false) {}

    //! instance
    static Manager *instance;

    //! Pointer to widget factory
    QPointer<NavigationWidgetFactory> widgetFactory;

    //! State mutex
    QMutex mutexState;

    //! code state/changes parser
    Parser parser;

    //! separate thread for the parser
    QThread parserThread;

    //! Internal manager state. \sa Manager::state
    bool state;

    //! there is some massive operation ongoing so temporary we should wait
    bool disableCodeParser;
};

// static variable initialization
Manager *ManagerPrivate::instance = 0;

///////////////////////////////// Manager //////////////////////////////////

Manager::Manager(QObject *parent)
    : QObject(parent),
    d_ptr(new ManagerPrivate())
{
    d_ptr->widgetFactory = NavigationWidgetFactory::instance();

    // register - to be able send between signal/slots
    qRegisterMetaType<QSharedPointer<QStandardItem> >("QSharedPointer<QStandardItem>");

    initialize();

    // start a separate thread for the parser
    d_ptr->parser.moveToThread(&d_ptr->parserThread);
    d_ptr->parserThread.start();

    // initial setup
    onProjectListChanged();
}

Manager::~Manager()
{
    d_ptr->parserThread.quit();
    d_ptr->parserThread.wait();
}

Manager *Manager::instance(QObject *parent)
{
    if (!ManagerPrivate::instance)
        ManagerPrivate::instance = new Manager(parent);
    return ManagerPrivate::instance;
}

bool Manager::canFetchMore(QStandardItem *item) const
{
    return d_ptr->parser.canFetchMore(item);
}

void Manager::fetchMore(QStandardItem *item, bool skipRoot)
{
    d_ptr->parser.fetchMore(item, skipRoot);
}

void Manager::initialize()
{
    // use Qt::QueuedConnection everywhere

    // widget factory signals
    connect(d_ptr->widgetFactory, SIGNAL(widgetIsCreated()),
            SLOT(onWidgetIsCreated()), Qt::QueuedConnection);

    // internal manager state is changed
    connect(this, SIGNAL(stateChanged(bool)), SLOT(onStateChanged(bool)), Qt::QueuedConnection);

    // connections to enable/disable navi widget factory
    ProjectExplorer::SessionManager *sessionManager =
        ProjectExplorer::ProjectExplorerPlugin::instance()->session();
    connect(sessionManager, SIGNAL(projectAdded(ProjectExplorer::Project*)),
            SLOT(onProjectListChanged()), Qt::QueuedConnection);
    connect(sessionManager, SIGNAL(projectRemoved(ProjectExplorer::Project*)),
            SLOT(onProjectListChanged()), Qt::QueuedConnection);

    Core::ICore *core = Core::ICore::instance();

    // connect to the progress manager for signals about Parsing tasks
    connect(core->progressManager(), SIGNAL(taskStarted(QString)),
            SLOT(onTaskStarted(QString)), Qt::QueuedConnection);
    connect(core->progressManager(), SIGNAL(allTasksFinished(QString)),
            SLOT(onAllTasksFinished(QString)), Qt::QueuedConnection);

    // when we signals that really document is updated - sent it to the parser
    connect(this, SIGNAL(requestDocumentUpdated(CPlusPlus::Document::Ptr)),
            &d_ptr->parser, SLOT(parseDocument(CPlusPlus::Document::Ptr)), Qt::QueuedConnection);

    // translate data update from the parser to listeners
    connect(&d_ptr->parser, SIGNAL(treeDataUpdate(QSharedPointer<QStandardItem>)),
            this, SLOT(onTreeDataUpdate(QSharedPointer<QStandardItem>)), Qt::QueuedConnection);

    // requet current state - immediately after a notification
    connect(this, SIGNAL(requestTreeDataUpdate()),
            &d_ptr->parser, SLOT(requestCurrentState()), Qt::QueuedConnection);

    // full reset request to parser
    connect(this, SIGNAL(requestResetCurrentState()),
            &d_ptr->parser, SLOT(resetDataToCurrentState()), Qt::QueuedConnection);

    // clear cache request
    connect(this, SIGNAL(requestClearCache()),
            &d_ptr->parser, SLOT(clearCache()), Qt::QueuedConnection);

    // clear full cache request
    connect(this, SIGNAL(requestClearCacheAll()),
            &d_ptr->parser, SLOT(clearCacheAll()), Qt::QueuedConnection);

    // flat mode request
    connect(this, SIGNAL(requestSetFlatMode(bool)),
            &d_ptr->parser, SLOT(setFlatMode(bool)), Qt::QueuedConnection);

    // connect to the cpp model manager for signals about document updates
    CPlusPlus::CppModelManagerInterface *codeModelManager
        = CPlusPlus::CppModelManagerInterface::instance();

    // when code manager signals that document is updated - handle it by ourselves
    connect(codeModelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
            SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)), Qt::QueuedConnection);
    //
    connect(codeModelManager, SIGNAL(aboutToRemoveFiles(QStringList)),
            &d_ptr->parser, SLOT(removeFiles(QStringList)), Qt::QueuedConnection);
}

bool Manager::state() const
{
    return d_ptr->state;
}

void Manager::setState(bool state)
{
    QMutexLocker locker(&d_ptr->mutexState);

    // boolean comparsion - should be done correctly by any compiler
    if (state == d_ptr->state)
        return;

    d_ptr->state = state;

    emit stateChanged(d_ptr->state);
}

void Manager::onWidgetIsCreated()
{
    // do nothing - continue to sleep
}

void Manager::onWidgetVisibilityIsChanged(bool visibility)
{
    // activate data handling - when 1st time 'Class View' will be open
    if (visibility)
        setState(true);
}

void Manager::onStateChanged(bool state)
{
    if (state) {
        // enabled - request a current snapshots etc?..
        emit requestResetCurrentState();
    } else {
        // disabled - clean parsers internal cache
        emit requestClearCacheAll();
    }
}

void Manager::onProjectListChanged()
{
    // do nothing if Manager is disabled
    if (!state())
        return;

    // update to the latest state
    requestTreeDataUpdate();
}

void Manager::onTaskStarted(const QString &type)
{
    if (type != CppTools::Constants::TASK_INDEX)
        return;

    // disable tree updates to speed up
    d_ptr->disableCodeParser = true;
}

void Manager::onAllTasksFinished(const QString &type)
{
    if (type != CppTools::Constants::TASK_INDEX)
        return;

    // parsing is finished, enable tree updates
    d_ptr->disableCodeParser = false;

    // do nothing if Manager is disabled
    if (!state())
        return;

    // any document might be changed, emit signal to clear cache
    emit requestClearCache();

    // request to update a tree to the current state
    emit requestResetCurrentState();
}

void Manager::onDocumentUpdated(CPlusPlus::Document::Ptr doc)
{
    // do nothing if Manager is disabled
    if (!state())
        return;

    // do nothing if updates are disabled
    if (d_ptr->disableCodeParser)
        return;

    emit requestDocumentUpdated(doc);
}

void Manager::gotoLocation(const QString &fileName, int line, int column)
{
    bool newEditor = false;
    TextEditor::BaseTextEditor::openEditorAt(fileName, line, column, QString(),
                                             Core::EditorManager::IgnoreNavigationHistory,
                                             &newEditor);
}

void Manager::gotoLocations(const QList<QVariant> &list)
{
    QSet<SymbolLocation> locations = Utils::roleToLocations(list);

    if (locations.count() == 0)
        return;

    QString fileName;
    int line = 0;
    int column = 0;
    bool currentPositionAvailable = false;

    // what is open now?
    Core::IEditor *editor = Core::EditorManager::instance()->currentEditor();
    if (editor) {
        // get current file name
        Core::IFile *file = editor->file();
        if (file)
            fileName = file->fileName();

        // if text file - what is current position?
        TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor *>(editor);
        if (textEditor) {
            // there is open currently text editor
            int position = textEditor->position();
            textEditor->convertPosition(position, &line, &column);
            currentPositionAvailable = true;
        }
    }

    // if there is something open - try to check, is it currently activated symbol?
    if (currentPositionAvailable) {
        SymbolLocation current(fileName, line, column);
        QSet<SymbolLocation>::const_iterator it = locations.find(current);
        QSet<SymbolLocation>::const_iterator end = locations.constEnd();
        // is it known location?
        if (it != end) {
            // found - do one additional step
            ++it;
            if (it == end)
                it = locations.begin();
            const SymbolLocation &found = *it;
            gotoLocation(found.fileName(), found.line(), found.column());
            return;
        }
    }

    // no success - open first item in the list
    const SymbolLocation loc = *locations.constBegin();

    gotoLocation(loc.fileName(), loc.line(), loc.column());
}

void Manager::onRequestTreeDataUpdate()
{
    // do nothing if Manager is disabled
    if (!state())
        return;

    emit requestTreeDataUpdate();
}

void Manager::setFlatMode(bool flat)
{
    emit requestSetFlatMode(flat);
}

void Manager::onTreeDataUpdate(QSharedPointer<QStandardItem> result)
{
    // do nothing if Manager is disabled
    if (!state())
        return;

    emit treeDataUpdate(result);
}

} // namespace Internal
} // namespace ClassView
<|endoftext|>


<commit_before>#include <cmath>
#include <gtest/gtest.h>
#include <limits>
#include <QCoreApplication>
#include <QStringList>
#include <unistd.h>
#include "dbus_observer.h"
#include "dbus_inverter_bridge.h"
#include "dbus_inverter_bridge_test.h"
#include "inverter.h"
#include "inverter_settings.h"
#include "power_info.h"
#include "test_helper.h"
#include "version.h"

TEST_F(DBusInverterBridgeTest, constructor)
{
	mInverter->meanPowerInfo()->setCurrent(1.4);
	mInverter->meanPowerInfo()->setPower(137);
	mInverter->meanPowerInfo()->setVoltage(342);
	mInverter->l2PowerInfo()->setCurrent(1.7);
	mInverter->l2PowerInfo()->setPower(138);
	mInverter->l2PowerInfo()->setVoltage(344);
	mInverter->l2PowerInfo()->setTotalEnergy(0);
	mSettings->setDeviceInstance(22);

	SetUpBridge();

	checkValue(QVariant(0), mDBusClient->getValue(mServiceName, "/Connected"));

	checkValue(QCoreApplication::arguments()[0],
			   mDBusClient->getValue(mServiceName, "/Mgmt/ProcessName"));
	checkValue(QString(VERSION),
			   mDBusClient->getValue(mServiceName, "/Mgmt/ProcessVersion"));
	checkValue(QString("10.0.1.4 - 3"),
			   mDBusClient->getValue(mServiceName, "/Mgmt/Connection"));
	checkValue(QVariant(2),
			   mDBusClient->getValue(mServiceName, "/Position"));
	// Note: we don't take the product ID from VE_PROD_ID_PV_INVERTER_FRONIUS,
	// because we want this test to fail if someone changes the define.
	checkValue(QString::number(0xA142),
			   mDBusClient->getValue(mServiceName, "/ProductId").toString());
	checkValue(QString("Fronius Symo 8.2-3-M"),
			   mDBusClient->getValue(mServiceName, "/ProductName"));
	checkValue(QString("Fronius Symo 8.2-3-M"),
			   mDBusClient->getValue(mServiceName, "/CustomName"));
	checkValue(QVariant(22),
			   mDBusClient->getValue(mServiceName, "/DeviceInstance"));

	checkValue(QVariant(1.4), mDBusClient->getValue(mServiceName, "/Ac/Current"));
	checkValue(QVariant(342.0), mDBusClient->getValue(mServiceName, "/Ac/Voltage"));
	checkValue(QVariant(137.0), mDBusClient->getValue(mServiceName, "/Ac/Power"));

	checkValue(QVariant(1.7), mDBusClient->getValue(mServiceName, "/Ac/L2/Current"));
	checkValue(QVariant(344.0), mDBusClient->getValue(mServiceName, "/Ac/L2/Voltage"));
	checkValue(QVariant(138.0), mDBusClient->getValue(mServiceName, "/Ac/L2/Power"));
}

TEST_F(DBusInverterBridgeTest, isConnected)
{
	SetUpBridge();

	EXPECT_FALSE(mInverter->isConnected());
	checkValue(QVariant(0), mDBusClient->getValue(mServiceName, "/Connected"));
	mInverter->setIsConnected(true);
	qWait(100);
	checkValue(QVariant(1), mDBusClient->getValue(mServiceName, "/Connected"));
}

TEST_F(DBusInverterBridgeTest, position)
{
	SetUpBridge();

	checkValue(QVariant(2), mDBusClient->getValue(mServiceName, "/Position"));
	mSettings->setPosition(Output);
	qWait(100);
	checkValue(QVariant(1), mDBusClient->getValue(mServiceName, "/Position"));
}

TEST_F(DBusInverterBridgeTest, deviceInstance)
{
	SetUpBridge();

	checkValue(QVariant(-1), mDBusClient->getValue(mServiceName, "/DeviceInstance"));
	mSettings->setDeviceInstance(21);
	qWait(100);
	checkValue(QVariant(21), mDBusClient->getValue(mServiceName, "/DeviceInstance"));
}

TEST_F(DBusInverterBridgeTest, acCurrent)
{
	SetUpBridge();
	checkValue(mInverter->meanPowerInfo(), "/Ac/Current", 3.41, 2.03, "2.0A",
			   "current");
}

TEST_F(DBusInverterBridgeTest, acVoltage)
{
	SetUpBridge();
	checkValue(mInverter->meanPowerInfo(), "/Ac/Voltage", 231, 232.9, "233V",
			   "voltage");
}

TEST_F(DBusInverterBridgeTest, acPower)
{
	SetUpBridge();
	checkValue(mInverter->meanPowerInfo(), "/Ac/Power", 3412, 3417.3, "3417W",
			   "power");
}

TEST_F(DBusInverterBridgeTest, acPowerNaN)
{
	SetUpBridge();
	checkValue(mInverter->meanPowerInfo(), "/Ac/Power", 3412, std::numeric_limits<double>::quiet_NaN(), "",
			   "power");
}

DBusInverterBridgeTest::DBusInverterBridgeTest():
	mServiceName("com.victronenergy.pvinverter.fronius_123_756")
{
}

void DBusInverterBridgeTest::SetUpTestCase()
{
	qRegisterMetaType<InverterPosition>("Position");
	qRegisterMetaType<InverterPhase>("Phase");
}

void DBusInverterBridgeTest::SetUp()
{
	mInverter.reset(new Inverter("10.0.1.4", 80, "3", 123, "756", "cn"));
	setupPowerInfo(mInverter->meanPowerInfo());
	setupPowerInfo(mInverter->l1PowerInfo());
	setupPowerInfo(mInverter->l2PowerInfo());
	setupPowerInfo(mInverter->l3PowerInfo());
	mSettings.reset(new InverterSettings(mInverter->deviceType(),
										 mInverter->uniqueId()));
	mSettings->setPosition(Input2);
	mSettings->setPhase(PhaseL2);
}

void DBusInverterBridgeTest::TearDown()
{
	mDBusClient.reset();
	mBridge.reset();
	mInverter.reset();
	mSettings.reset();
}

void DBusInverterBridgeTest::SetUpBridge()
{
	mBridge.reset(new DBusInverterBridge(mInverter.data(), mSettings.data()));
	mDBusClient.reset(new DBusObserver(mServiceName));
	while (!mDBusClient->isInitialized(mServiceName)) {
		QCoreApplication::processEvents(QEventLoop::AllEvents, 10);
	}
}

void DBusInverterBridgeTest::setupPowerInfo(PowerInfo *pi)
{
	pi->setVoltage(0);
	pi->setCurrent(0);
	pi->setPower(0);
	pi->setTotalEnergy(0);
}

void DBusInverterBridgeTest::checkValue(PowerInfo *pi, const QString &path,
										double v0, double v1,
										const QString &text,
										const char *property)
{
	pi->setProperty(property, v0);
	qWait(100);
	checkValue(QVariant(v0), mDBusClient->getValue(mServiceName, path));

	pi->setProperty(property, v1);
	qWait(100);
	QVariant vv1;
	if (!std::isnan(v1))
		vv1 = QVariant(v1);
	checkValue(vv1, mDBusClient->getValue(mServiceName, path));
	EXPECT_EQ(text, mDBusClient->getText(mServiceName, path));
}

void DBusInverterBridgeTest::checkValue(const QVariant &expected,
										const QVariant &actual)
{
	EXPECT_EQ(expected.type(), actual.type());
	EXPECT_EQ(expected, actual);
}
<commit_msg>Fixed unit tests<commit_after>#include <cmath>
#include <gtest/gtest.h>
#include <limits>
#include <QCoreApplication>
#include <QStringList>
#include <unistd.h>
#include "dbus_observer.h"
#include "dbus_inverter_bridge.h"
#include "dbus_inverter_bridge_test.h"
#include "inverter.h"
#include "inverter_settings.h"
#include "power_info.h"
#include "test_helper.h"
#include "version.h"

TEST_F(DBusInverterBridgeTest, constructor)
{
	mInverter->meanPowerInfo()->setCurrent(1.4);
	mInverter->meanPowerInfo()->setPower(137);
	mInverter->meanPowerInfo()->setVoltage(342);
	mInverter->l2PowerInfo()->setCurrent(1.7);
	mInverter->l2PowerInfo()->setPower(138);
	mInverter->l2PowerInfo()->setVoltage(344);
	mInverter->l2PowerInfo()->setTotalEnergy(0);
	mInverter->setDeviceInstance(22);

	SetUpBridge();

	checkValue(QVariant(0), mDBusClient->getValue(mServiceName, "/Connected"));

	checkValue(QCoreApplication::arguments()[0],
			   mDBusClient->getValue(mServiceName, "/Mgmt/ProcessName"));
	checkValue(QString(VERSION),
			   mDBusClient->getValue(mServiceName, "/Mgmt/ProcessVersion"));
	checkValue(QString("10.0.1.4 - 3"),
			   mDBusClient->getValue(mServiceName, "/Mgmt/Connection"));
	checkValue(QVariant(2),
			   mDBusClient->getValue(mServiceName, "/Position"));
	// Note: we don't take the product ID from VE_PROD_ID_PV_INVERTER_FRONIUS,
	// because we want this test to fail if someone changes the define.
	checkValue(QString::number(0xA142),
			   mDBusClient->getValue(mServiceName, "/ProductId").toString());
	checkValue(QString("Fronius Symo 8.2-3-M"),
			   mDBusClient->getValue(mServiceName, "/ProductName"));
	checkValue(QString("Fronius Symo 8.2-3-M"),
			   mDBusClient->getValue(mServiceName, "/CustomName"));
	checkValue(QVariant(22),
			   mDBusClient->getValue(mServiceName, "/DeviceInstance"));

	checkValue(QVariant(1.4), mDBusClient->getValue(mServiceName, "/Ac/Current"));
	checkValue(QVariant(342.0), mDBusClient->getValue(mServiceName, "/Ac/Voltage"));
	checkValue(QVariant(137.0), mDBusClient->getValue(mServiceName, "/Ac/Power"));

	checkValue(QVariant(1.7), mDBusClient->getValue(mServiceName, "/Ac/L2/Current"));
	checkValue(QVariant(344.0), mDBusClient->getValue(mServiceName, "/Ac/L2/Voltage"));
	checkValue(QVariant(138.0), mDBusClient->getValue(mServiceName, "/Ac/L2/Power"));
}

TEST_F(DBusInverterBridgeTest, isConnected)
{
	SetUpBridge();

	EXPECT_FALSE(mInverter->isConnected());
	checkValue(QVariant(0), mDBusClient->getValue(mServiceName, "/Connected"));
	mInverter->setIsConnected(true);
	qWait(100);
	checkValue(QVariant(1), mDBusClient->getValue(mServiceName, "/Connected"));
}

TEST_F(DBusInverterBridgeTest, position)
{
	SetUpBridge();

	checkValue(QVariant(2), mDBusClient->getValue(mServiceName, "/Position"));
	mSettings->setPosition(Output);
	qWait(100);
	checkValue(QVariant(1), mDBusClient->getValue(mServiceName, "/Position"));
}

TEST_F(DBusInverterBridgeTest, deviceInstance)
{
	SetUpBridge();

	checkValue(QVariant(-1), mDBusClient->getValue(mServiceName, "/DeviceInstance"));
	mInverter->setDeviceInstance(21);
	qWait(100);
	checkValue(QVariant(21), mDBusClient->getValue(mServiceName, "/DeviceInstance"));
}

TEST_F(DBusInverterBridgeTest, acCurrent)
{
	SetUpBridge();
	checkValue(mInverter->meanPowerInfo(), "/Ac/Current", 3.41, 2.03, "2.0A",
			   "current");
}

TEST_F(DBusInverterBridgeTest, acVoltage)
{
	SetUpBridge();
	checkValue(mInverter->meanPowerInfo(), "/Ac/Voltage", 231, 232.9, "233V",
			   "voltage");
}

TEST_F(DBusInverterBridgeTest, acPower)
{
	SetUpBridge();
	checkValue(mInverter->meanPowerInfo(), "/Ac/Power", 3412, 3417.3, "3417W",
			   "power");
}

TEST_F(DBusInverterBridgeTest, acPowerNaN)
{
	SetUpBridge();
	checkValue(mInverter->meanPowerInfo(), "/Ac/Power", 3412, std::numeric_limits<double>::quiet_NaN(), "",
			   "power");
}

DBusInverterBridgeTest::DBusInverterBridgeTest():
	mServiceName("com.victronenergy.pvinverter.fronius_123_756")
{
}

void DBusInverterBridgeTest::SetUpTestCase()
{
	qRegisterMetaType<InverterPosition>("Position");
	qRegisterMetaType<InverterPhase>("Phase");
}

void DBusInverterBridgeTest::SetUp()
{
	mInverter.reset(new Inverter("10.0.1.4", 80, "3", 123, "756", "cn"));
	setupPowerInfo(mInverter->meanPowerInfo());
	setupPowerInfo(mInverter->l1PowerInfo());
	setupPowerInfo(mInverter->l2PowerInfo());
	setupPowerInfo(mInverter->l3PowerInfo());
	mSettings.reset(new InverterSettings(mInverter->deviceType(),
										 mInverter->uniqueId()));
	mSettings->setPosition(Input2);
	mSettings->setPhase(PhaseL2);
}

void DBusInverterBridgeTest::TearDown()
{
	mDBusClient.reset();
	mBridge.reset();
	mInverter.reset();
	mSettings.reset();
}

void DBusInverterBridgeTest::SetUpBridge()
{
	mBridge.reset(new DBusInverterBridge(mInverter.data(), mSettings.data()));
	mDBusClient.reset(new DBusObserver(mServiceName));
	while (!mDBusClient->isInitialized(mServiceName)) {
		QCoreApplication::processEvents(QEventLoop::AllEvents, 10);
	}
}

void DBusInverterBridgeTest::setupPowerInfo(PowerInfo *pi)
{
	pi->setVoltage(0);
	pi->setCurrent(0);
	pi->setPower(0);
	pi->setTotalEnergy(0);
}

void DBusInverterBridgeTest::checkValue(PowerInfo *pi, const QString &path,
										double v0, double v1,
										const QString &text,
										const char *property)
{
	pi->setProperty(property, v0);
	qWait(100);
	checkValue(QVariant(v0), mDBusClient->getValue(mServiceName, path));

	pi->setProperty(property, v1);
	qWait(100);
	QVariant vv1;
	if (!std::isnan(v1))
		vv1 = QVariant(v1);
	checkValue(vv1, mDBusClient->getValue(mServiceName, path));
	EXPECT_EQ(text, mDBusClient->getText(mServiceName, path));
}

void DBusInverterBridgeTest::checkValue(const QVariant &expected,
										const QVariant &actual)
{
	EXPECT_EQ(expected.type(), actual.type());
	EXPECT_EQ(expected, actual);
}
<|endoftext|>