<commit_before>//===- Core/Resolver.cpp - Resolves Atom References -----------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lld/Core/Atom.h"
#include "lld/Core/ArchiveLibraryFile.h"
#include "lld/Core/File.h"
#include "lld/Core/SharedLibraryFile.h"
#include "lld/Core/Instrumentation.h"
#include "lld/Core/LLVM.h"
#include "lld/Core/Resolver.h"
#include "lld/Core/SymbolTable.h"
#include "lld/Core/LinkingContext.h"
#include "lld/Core/UndefinedAtom.h"

#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"

#include <algorithm>
#include <cassert>
#include <vector>

namespace lld {

namespace {

/// This is used as a filter function to std::remove_if to dead strip atoms.
class NotLive {
public:
  explicit NotLive(const llvm::DenseSet<const Atom *> &la) : _liveAtoms(la) {}

  bool operator()(const Atom *atom) const {
    // don't remove if live
    if (_liveAtoms.count(atom))
      return false;
    // don't remove if marked never-dead-strip
    if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom))
      if (defAtom->deadStrip() == DefinedAtom::deadStripNever)
        return false;
    // do remove this atom
    return true;
  }

private:
  const llvm::DenseSet<const Atom *> &_liveAtoms;
};

/// This is used as a filter function to std::remove_if to coalesced atoms.
class AtomCoalescedAway {
public:
  explicit AtomCoalescedAway(SymbolTable &sym) : _symbolTable(sym) {}

  bool operator()(const Atom *atom) const {
    const Atom *rep = _symbolTable.replacement(atom);
    return rep != atom;
  }

private:
  SymbolTable &_symbolTable;
};

} // namespace

// called before the first atom in any file is added with doAtom()
void Resolver::doFile(const File &file) {}

void Resolver::handleFile(const File &file) {
  uint32_t resolverState = Resolver::StateNoChange;
  const SharedLibraryFile *sharedLibraryFile =
      llvm::dyn_cast<SharedLibraryFile>(&file);

  doFile(file);
  for (const DefinedAtom *atom : file.defined()) {
    doDefinedAtom(*atom);
    resolverState |= StateNewDefinedAtoms;
  }
  if (!sharedLibraryFile ||
      _context.addUndefinedAtomsFromSharedLibrary(sharedLibraryFile)) {
    for (const UndefinedAtom *undefAtom : file.undefined()) {
      doUndefinedAtom(*undefAtom);
      resolverState |= StateNewUndefinedAtoms;
      // If the undefined symbol has an alternative name, try to resolve the
      // symbol with the name to give it a second chance. This feature is used
      // for COFF "weak external" symbol.
      if (!_symbolTable.isDefined(undefAtom->name())) {
        if (const UndefinedAtom *fallbackAtom = undefAtom->fallback()) {
          doUndefinedAtom(*fallbackAtom);
          _symbolTable.addReplacement(undefAtom, fallbackAtom);
        }
      }
    }
  }
  for (const SharedLibraryAtom *shlibAtom : file.sharedLibrary()) {
    doSharedLibraryAtom(*shlibAtom);
    resolverState |= StateNewSharedLibraryAtoms;
  }
  for (const AbsoluteAtom *absAtom : file.absolute()) {
    doAbsoluteAtom(*absAtom);
    resolverState |= StateNewAbsoluteAtoms;
  }
  _context.setResolverState(resolverState);
}

void Resolver::forEachUndefines(UndefCallback callback,
                                bool searchForOverrides) {
  // Handle normal archives
  int64_t undefineGenCount = 0;
  do {
    undefineGenCount = _symbolTable.size();
    std::vector<const UndefinedAtom *> undefines;
    _symbolTable.undefines(undefines);
    for (const UndefinedAtom *undefAtom : undefines) {
      StringRef undefName = undefAtom->name();
      // load for previous undefine may also have loaded this undefine
      if (!_symbolTable.isDefined(undefName))
        callback(undefName, false);
    }
    // search libraries for overrides of common symbols
    if (searchForOverrides) {
      std::vector<StringRef> tentDefNames;
      _symbolTable.tentativeDefinitions(tentDefNames);
      for (StringRef tentDefName : tentDefNames) {
        // Load for previous tentative may also have loaded
        // something that overrode this tentative, so always check.
        const Atom *curAtom = _symbolTable.findByName(tentDefName);
        assert(curAtom != nullptr);
        if (const DefinedAtom *curDefAtom = dyn_cast<DefinedAtom>(curAtom)) {
          if (curDefAtom->merge() == DefinedAtom::mergeAsTentative)
            callback(tentDefName, true);
        }
      }
    }
  } while (undefineGenCount != _symbolTable.size());
}

void Resolver::handleArchiveFile(const File &file) {
  const ArchiveLibraryFile *archiveFile = dyn_cast<ArchiveLibraryFile>(&file);
  auto callback = [&](StringRef undefName, bool dataSymbolOnly) {
    if (const File *member = archiveFile->find(undefName, dataSymbolOnly)) {
      member->setOrdinal(_context.getNextOrdinalAndIncrement());
      handleFile(*member);
    }
  };
  bool searchForOverrides =
      _context.searchArchivesToOverrideTentativeDefinitions();
  forEachUndefines(callback, searchForOverrides);
}

void Resolver::handleSharedLibrary(const File &file) {
  // Add all the atoms from the shared library
  const SharedLibraryFile *sharedLibrary = dyn_cast<SharedLibraryFile>(&file);
  handleFile(*sharedLibrary);

  auto callback = [&](StringRef undefName, bool dataSymbolOnly) {
    if (const SharedLibraryAtom *shAtom =
            sharedLibrary->exports(undefName, dataSymbolOnly))
      doSharedLibraryAtom(*shAtom);
  };
  bool searchForOverrides =
      _context.searchSharedLibrariesToOverrideTentativeDefinitions();
  forEachUndefines(callback, searchForOverrides);
}

void Resolver::doUndefinedAtom(const UndefinedAtom &atom) {
  DEBUG_WITH_TYPE("resolver", llvm::dbgs()
                    << "       UndefinedAtom: "
                    << llvm::format("0x%09lX", &atom)
                    << ", name="
                    << atom.name()
                    << "\n");

  // add to list of known atoms
  _atoms.push_back(&atom);

  // tell symbol table
  _symbolTable.add(atom);
}

// called on each atom when a file is added
void Resolver::doDefinedAtom(const DefinedAtom &atom) {
  DEBUG_WITH_TYPE("resolver", llvm::dbgs()
                    << "         DefinedAtom: "
                    << llvm::format("0x%09lX", &atom)
                    << ", file=#"
                    << atom.file().ordinal()
                    << ", atom=#"
                    << atom.ordinal()
                    << ", name="
                    << atom.name()
                    << "\n");

  // Verify on zero-size atoms are pinned to start or end of section.
  switch (atom.sectionPosition()) {
  case DefinedAtom::sectionPositionStart:
  case DefinedAtom::sectionPositionEnd:
    assert(atom.size() == 0);
    break;
  case DefinedAtom::sectionPositionEarly:
  case DefinedAtom::sectionPositionAny:
    break;
  }

  // add to list of known atoms
  _atoms.push_back(&atom);

  // tell symbol table
  _symbolTable.add(atom);

  if (_context.deadStrip()) {
    // add to set of dead-strip-roots, all symbols that
    // the compiler marks as don't strip
    if (atom.deadStrip() == DefinedAtom::deadStripNever)
      _deadStripRoots.insert(&atom);
  }
}

void Resolver::doSharedLibraryAtom(const SharedLibraryAtom &atom) {
  DEBUG_WITH_TYPE("resolver", llvm::dbgs()
                    << "   SharedLibraryAtom: "
                    << llvm::format("0x%09lX", &atom)
                    << ", name="
                    << atom.name()
                    << "\n");

  // add to list of known atoms
  _atoms.push_back(&atom);

  // tell symbol table
  _symbolTable.add(atom);
}

void Resolver::doAbsoluteAtom(const AbsoluteAtom &atom) {
  DEBUG_WITH_TYPE("resolver", llvm::dbgs()
                    << "       AbsoluteAtom: "
                    << llvm::format("0x%09lX", &atom)
                    << ", name="
                    << atom.name()
                    << "\n");

  // add to list of known atoms
  _atoms.push_back(&atom);

  // tell symbol table
  if (atom.scope() != Atom::scopeTranslationUnit)
    _symbolTable.add(atom);
}

// utility to add a vector of atoms
void Resolver::addAtoms(const std::vector<const DefinedAtom *> &newAtoms) {
  for (const DefinedAtom *newAtom : newAtoms)
    this->doDefinedAtom(*newAtom);
}

// Keep adding atoms until _context.nextFile() returns an error. This function
// is where undefined atoms are resolved.
bool Resolver::resolveUndefines() {
  ScopedTask task(getDefaultDomain(), "resolveUndefines");

  for (;;) {
    ErrorOr<File &> file = _context.nextFile();
    _context.setResolverState(Resolver::StateNoChange);
    if (error_code(file) == InputGraphError::no_more_files)
      return true;
    if (!file) {
      llvm::errs() << "Error occurred in nextFile: "
                   << error_code(file).message() << "\n";
      return false;
    }

    switch (file->kind()) {
    case File::kindObject:
      assert(!file->hasOrdinal());
      file->setOrdinal(_context.getNextOrdinalAndIncrement());
      handleFile(*file);
      break;
    case File::kindArchiveLibrary:
      if (!file->hasOrdinal())
        file->setOrdinal(_context.getNextOrdinalAndIncrement());
      handleArchiveFile(*file);
      break;
    case File::kindSharedLibrary:
      if (!file->hasOrdinal())
        file->setOrdinal(_context.getNextOrdinalAndIncrement());
      handleSharedLibrary(*file);
      break;
    }
  }
}

// switch all references to undefined or coalesced away atoms
// to the new defined atom
void Resolver::updateReferences() {
  ScopedTask task(getDefaultDomain(), "updateReferences");
  for (const Atom *atom : _atoms) {
    if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom)) {
      for (const Reference *ref : *defAtom) {
        const Atom *newTarget = _symbolTable.replacement(ref->target());
        const_cast<Reference *>(ref)->setTarget(newTarget);
      }
    }
  }
}

// For dead code stripping, recursively mark atoms "live"
void Resolver::markLive(const Atom &atom) {
  // Mark the atom is live. If it's already marked live, then stop recursion.
  auto exists = _liveAtoms.insert(&atom);
  if (!exists.second)
    return;

  // Mark all atoms it references as live
  if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(&atom))
    for (const Reference *ref : *defAtom)
      if (const Atom *target = ref->target())
        this->markLive(*target);
}

// remove all atoms not actually used
void Resolver::deadStripOptimize() {
  ScopedTask task(getDefaultDomain(), "deadStripOptimize");
  // only do this optimization with -dead_strip
  if (!_context.deadStrip())
    return;
  assert(_liveAtoms.empty());

  // By default, shared libraries are built with all globals as dead strip roots
  if (_context.globalsAreDeadStripRoots()) {
    for (const Atom *atom : _atoms) {
      const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom);
      if (defAtom == nullptr)
        continue;
      if (defAtom->scope() == DefinedAtom::scopeGlobal)
        _deadStripRoots.insert(defAtom);
    }
  }

  // Or, use list of names that are dead stip roots.
  for (const StringRef &name : _context.deadStripRoots()) {
    const Atom *symAtom = _symbolTable.findByName(name);
    assert(symAtom);
    _deadStripRoots.insert(symAtom);
  }

  // mark all roots as live, and recursively all atoms they reference
  for (const Atom *dsrAtom : _deadStripRoots)
    this->markLive(*dsrAtom);

  // now remove all non-live atoms from _atoms
  _atoms.erase(
      std::remove_if(_atoms.begin(), _atoms.end(), NotLive(_liveAtoms)),
      _atoms.end());
}

// error out if some undefines remain
bool Resolver::checkUndefines(bool final) {
  // when using LTO, undefines are checked after bitcode is optimized
  if (_haveLLVMObjs && !final)
    return false;

  // build vector of remaining undefined symbols
  std::vector<const UndefinedAtom *> undefinedAtoms;
  _symbolTable.undefines(undefinedAtoms);
  if (_context.deadStrip()) {
    // When dead code stripping, we don't care if dead atoms are undefined.
    undefinedAtoms.erase(std::remove_if(undefinedAtoms.begin(),
                                        undefinedAtoms.end(),
                                        NotLive(_liveAtoms)),
                         undefinedAtoms.end());
  }

  // error message about missing symbols
  if (!undefinedAtoms.empty()) {
    // FIXME: need diagnostics interface for writing error messages
    bool foundUndefines = false;
    for (const UndefinedAtom *undefAtom : undefinedAtoms) {
      const File &f = undefAtom->file();

      // Skip over a weak symbol.
      if (undefAtom->canBeNull() != UndefinedAtom::canBeNullNever)
        continue;

      // If this is a library and undefined symbols are allowed on the
      // target platform, skip over it.
      if (isa<SharedLibraryFile>(f) && _context.allowShlibUndefines())
        continue;

      // If the undefine is coalesced away, skip over it.
      if (_symbolTable.replacement(undefAtom) != undefAtom)
        continue;

      // Seems like this symbol is undefined. Warn that.
      foundUndefines = true;
      if (_context.printRemainingUndefines()) {
        llvm::errs() << "Undefined Symbol: " << undefAtom->file().path()
                     << " : " << undefAtom->name() << "\n";
      }
    }
    if (foundUndefines) {
      if (_context.printRemainingUndefines())
        llvm::errs() << "symbol(s) not found\n";
      return true;
    }
  }
  return false;
}

// remove from _atoms all coaleseced away atoms
void Resolver::removeCoalescedAwayAtoms() {
  ScopedTask task(getDefaultDomain(), "removeCoalescedAwayAtoms");
  _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(),
                              AtomCoalescedAway(_symbolTable)),
               _atoms.end());
}

void Resolver::linkTimeOptimize() {
  // FIX ME
}

bool Resolver::resolve() {
  if (!this->resolveUndefines())
    return false;
  this->updateReferences();
  this->deadStripOptimize();
  if (this->checkUndefines(false))
    if (!_context.allowRemainingUndefines())
      return false;
  this->removeCoalescedAwayAtoms();
  this->linkTimeOptimize();
  this->_result->addAtoms(_atoms);
  return true;
}

void Resolver::MergedFile::addAtom(const Atom &atom) {
  if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(&atom)) {
    _definedAtoms._atoms.push_back(defAtom);
  } else if (const UndefinedAtom *undefAtom = dyn_cast<UndefinedAtom>(&atom)) {
    _undefinedAtoms._atoms.push_back(undefAtom);
  } else if (const SharedLibraryAtom *slAtom =
                 dyn_cast<SharedLibraryAtom>(&atom)) {
    _sharedLibraryAtoms._atoms.push_back(slAtom);
  } else if (const AbsoluteAtom *abAtom = dyn_cast<AbsoluteAtom>(&atom)) {
    _absoluteAtoms._atoms.push_back(abAtom);
  } else {
    llvm_unreachable("atom has unknown definition kind");
  }
}

MutableFile::DefinedAtomRange Resolver::MergedFile::definedAtoms() {
  return range<std::vector<const DefinedAtom *>::iterator>(
      _definedAtoms._atoms.begin(), _definedAtoms._atoms.end());
}

void Resolver::MergedFile::addAtoms(std::vector<const Atom *> &all) {
  ScopedTask task(getDefaultDomain(), "addAtoms");
  DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Resolver final atom list:\n");
  for (const Atom *atom : all) {
    DEBUG_WITH_TYPE("resolver", llvm::dbgs()
                    << llvm::format("    0x%09lX", atom)
                    << ", name="
                    << atom->name()
                    << "\n");
    this->addAtom(*atom);
  }
}

} // namespace lld
<commit_msg>Typo<commit_after>//===- Core/Resolver.cpp - Resolves Atom References -----------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lld/Core/Atom.h"
#include "lld/Core/ArchiveLibraryFile.h"
#include "lld/Core/File.h"
#include "lld/Core/SharedLibraryFile.h"
#include "lld/Core/Instrumentation.h"
#include "lld/Core/LLVM.h"
#include "lld/Core/Resolver.h"
#include "lld/Core/SymbolTable.h"
#include "lld/Core/LinkingContext.h"
#include "lld/Core/UndefinedAtom.h"

#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"

#include <algorithm>
#include <cassert>
#include <vector>

namespace lld {

namespace {

/// This is used as a filter function to std::remove_if to dead strip atoms.
class NotLive {
public:
  explicit NotLive(const llvm::DenseSet<const Atom *> &la) : _liveAtoms(la) {}

  bool operator()(const Atom *atom) const {
    // don't remove if live
    if (_liveAtoms.count(atom))
      return false;
    // don't remove if marked never-dead-strip
    if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom))
      if (defAtom->deadStrip() == DefinedAtom::deadStripNever)
        return false;
    // do remove this atom
    return true;
  }

private:
  const llvm::DenseSet<const Atom *> &_liveAtoms;
};

/// This is used as a filter function to std::remove_if to coalesced atoms.
class AtomCoalescedAway {
public:
  explicit AtomCoalescedAway(SymbolTable &sym) : _symbolTable(sym) {}

  bool operator()(const Atom *atom) const {
    const Atom *rep = _symbolTable.replacement(atom);
    return rep != atom;
  }

private:
  SymbolTable &_symbolTable;
};

} // namespace

// called before the first atom in any file is added with doAtom()
void Resolver::doFile(const File &file) {}

void Resolver::handleFile(const File &file) {
  uint32_t resolverState = Resolver::StateNoChange;
  const SharedLibraryFile *sharedLibraryFile =
      llvm::dyn_cast<SharedLibraryFile>(&file);

  doFile(file);
  for (const DefinedAtom *atom : file.defined()) {
    doDefinedAtom(*atom);
    resolverState |= StateNewDefinedAtoms;
  }
  if (!sharedLibraryFile ||
      _context.addUndefinedAtomsFromSharedLibrary(sharedLibraryFile)) {
    for (const UndefinedAtom *undefAtom : file.undefined()) {
      doUndefinedAtom(*undefAtom);
      resolverState |= StateNewUndefinedAtoms;
      // If the undefined symbol has an alternative name, try to resolve the
      // symbol with the name to give it a second chance. This feature is used
      // for COFF "weak external" symbol.
      if (!_symbolTable.isDefined(undefAtom->name())) {
        if (const UndefinedAtom *fallbackAtom = undefAtom->fallback()) {
          doUndefinedAtom(*fallbackAtom);
          _symbolTable.addReplacement(undefAtom, fallbackAtom);
        }
      }
    }
  }
  for (const SharedLibraryAtom *shlibAtom : file.sharedLibrary()) {
    doSharedLibraryAtom(*shlibAtom);
    resolverState |= StateNewSharedLibraryAtoms;
  }
  for (const AbsoluteAtom *absAtom : file.absolute()) {
    doAbsoluteAtom(*absAtom);
    resolverState |= StateNewAbsoluteAtoms;
  }
  _context.setResolverState(resolverState);
}

void Resolver::forEachUndefines(UndefCallback callback,
                                bool searchForOverrides) {
  // Handle normal archives
  int64_t undefineGenCount = 0;
  do {
    undefineGenCount = _symbolTable.size();
    std::vector<const UndefinedAtom *> undefines;
    _symbolTable.undefines(undefines);
    for (const UndefinedAtom *undefAtom : undefines) {
      StringRef undefName = undefAtom->name();
      // load for previous undefine may also have loaded this undefine
      if (!_symbolTable.isDefined(undefName))
        callback(undefName, false);
    }
    // search libraries for overrides of common symbols
    if (searchForOverrides) {
      std::vector<StringRef> tentDefNames;
      _symbolTable.tentativeDefinitions(tentDefNames);
      for (StringRef tentDefName : tentDefNames) {
        // Load for previous tentative may also have loaded
        // something that overrode this tentative, so always check.
        const Atom *curAtom = _symbolTable.findByName(tentDefName);
        assert(curAtom != nullptr);
        if (const DefinedAtom *curDefAtom = dyn_cast<DefinedAtom>(curAtom)) {
          if (curDefAtom->merge() == DefinedAtom::mergeAsTentative)
            callback(tentDefName, true);
        }
      }
    }
  } while (undefineGenCount != _symbolTable.size());
}

void Resolver::handleArchiveFile(const File &file) {
  const ArchiveLibraryFile *archiveFile = dyn_cast<ArchiveLibraryFile>(&file);
  auto callback = [&](StringRef undefName, bool dataSymbolOnly) {
    if (const File *member = archiveFile->find(undefName, dataSymbolOnly)) {
      member->setOrdinal(_context.getNextOrdinalAndIncrement());
      handleFile(*member);
    }
  };
  bool searchForOverrides =
      _context.searchArchivesToOverrideTentativeDefinitions();
  forEachUndefines(callback, searchForOverrides);
}

void Resolver::handleSharedLibrary(const File &file) {
  // Add all the atoms from the shared library
  const SharedLibraryFile *sharedLibrary = dyn_cast<SharedLibraryFile>(&file);
  handleFile(*sharedLibrary);

  auto callback = [&](StringRef undefName, bool dataSymbolOnly) {
    if (const SharedLibraryAtom *shAtom =
            sharedLibrary->exports(undefName, dataSymbolOnly))
      doSharedLibraryAtom(*shAtom);
  };
  bool searchForOverrides =
      _context.searchSharedLibrariesToOverrideTentativeDefinitions();
  forEachUndefines(callback, searchForOverrides);
}

void Resolver::doUndefinedAtom(const UndefinedAtom &atom) {
  DEBUG_WITH_TYPE("resolver", llvm::dbgs()
                    << "       UndefinedAtom: "
                    << llvm::format("0x%09lX", &atom)
                    << ", name="
                    << atom.name()
                    << "\n");

  // add to list of known atoms
  _atoms.push_back(&atom);

  // tell symbol table
  _symbolTable.add(atom);
}

// called on each atom when a file is added
void Resolver::doDefinedAtom(const DefinedAtom &atom) {
  DEBUG_WITH_TYPE("resolver", llvm::dbgs()
                    << "         DefinedAtom: "
                    << llvm::format("0x%09lX", &atom)
                    << ", file=#"
                    << atom.file().ordinal()
                    << ", atom=#"
                    << atom.ordinal()
                    << ", name="
                    << atom.name()
                    << "\n");

  // Verify on zero-size atoms are pinned to start or end of section.
  switch (atom.sectionPosition()) {
  case DefinedAtom::sectionPositionStart:
  case DefinedAtom::sectionPositionEnd:
    assert(atom.size() == 0);
    break;
  case DefinedAtom::sectionPositionEarly:
  case DefinedAtom::sectionPositionAny:
    break;
  }

  // add to list of known atoms
  _atoms.push_back(&atom);

  // tell symbol table
  _symbolTable.add(atom);

  if (_context.deadStrip()) {
    // add to set of dead-strip-roots, all symbols that
    // the compiler marks as don't strip
    if (atom.deadStrip() == DefinedAtom::deadStripNever)
      _deadStripRoots.insert(&atom);
  }
}

void Resolver::doSharedLibraryAtom(const SharedLibraryAtom &atom) {
  DEBUG_WITH_TYPE("resolver", llvm::dbgs()
                    << "   SharedLibraryAtom: "
                    << llvm::format("0x%09lX", &atom)
                    << ", name="
                    << atom.name()
                    << "\n");

  // add to list of known atoms
  _atoms.push_back(&atom);

  // tell symbol table
  _symbolTable.add(atom);
}

void Resolver::doAbsoluteAtom(const AbsoluteAtom &atom) {
  DEBUG_WITH_TYPE("resolver", llvm::dbgs()
                    << "       AbsoluteAtom: "
                    << llvm::format("0x%09lX", &atom)
                    << ", name="
                    << atom.name()
                    << "\n");

  // add to list of known atoms
  _atoms.push_back(&atom);

  // tell symbol table
  if (atom.scope() != Atom::scopeTranslationUnit)
    _symbolTable.add(atom);
}

// utility to add a vector of atoms
void Resolver::addAtoms(const std::vector<const DefinedAtom *> &newAtoms) {
  for (const DefinedAtom *newAtom : newAtoms)
    this->doDefinedAtom(*newAtom);
}

// Keep adding atoms until _context.nextFile() returns an error. This function
// is where undefined atoms are resolved.
bool Resolver::resolveUndefines() {
  ScopedTask task(getDefaultDomain(), "resolveUndefines");

  for (;;) {
    ErrorOr<File &> file = _context.nextFile();
    _context.setResolverState(Resolver::StateNoChange);
    if (error_code(file) == InputGraphError::no_more_files)
      return true;
    if (!file) {
      llvm::errs() << "Error occurred in nextFile: "
                   << error_code(file).message() << "\n";
      return false;
    }

    switch (file->kind()) {
    case File::kindObject:
      assert(!file->hasOrdinal());
      file->setOrdinal(_context.getNextOrdinalAndIncrement());
      handleFile(*file);
      break;
    case File::kindArchiveLibrary:
      if (!file->hasOrdinal())
        file->setOrdinal(_context.getNextOrdinalAndIncrement());
      handleArchiveFile(*file);
      break;
    case File::kindSharedLibrary:
      if (!file->hasOrdinal())
        file->setOrdinal(_context.getNextOrdinalAndIncrement());
      handleSharedLibrary(*file);
      break;
    }
  }
}

// switch all references to undefined or coalesced away atoms
// to the new defined atom
void Resolver::updateReferences() {
  ScopedTask task(getDefaultDomain(), "updateReferences");
  for (const Atom *atom : _atoms) {
    if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom)) {
      for (const Reference *ref : *defAtom) {
        const Atom *newTarget = _symbolTable.replacement(ref->target());
        const_cast<Reference *>(ref)->setTarget(newTarget);
      }
    }
  }
}

// For dead code stripping, recursively mark atoms "live"
void Resolver::markLive(const Atom &atom) {
  // Mark the atom is live. If it's already marked live, then stop recursion.
  auto exists = _liveAtoms.insert(&atom);
  if (!exists.second)
    return;

  // Mark all atoms it references as live
  if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(&atom))
    for (const Reference *ref : *defAtom)
      if (const Atom *target = ref->target())
        this->markLive(*target);
}

// remove all atoms not actually used
void Resolver::deadStripOptimize() {
  ScopedTask task(getDefaultDomain(), "deadStripOptimize");
  // only do this optimization with -dead_strip
  if (!_context.deadStrip())
    return;
  assert(_liveAtoms.empty());

  // By default, shared libraries are built with all globals as dead strip roots
  if (_context.globalsAreDeadStripRoots()) {
    for (const Atom *atom : _atoms) {
      const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom);
      if (defAtom == nullptr)
        continue;
      if (defAtom->scope() == DefinedAtom::scopeGlobal)
        _deadStripRoots.insert(defAtom);
    }
  }

  // Or, use list of names that are dead strip roots.
  for (const StringRef &name : _context.deadStripRoots()) {
    const Atom *symAtom = _symbolTable.findByName(name);
    assert(symAtom);
    _deadStripRoots.insert(symAtom);
  }

  // mark all roots as live, and recursively all atoms they reference
  for (const Atom *dsrAtom : _deadStripRoots)
    this->markLive(*dsrAtom);

  // now remove all non-live atoms from _atoms
  _atoms.erase(
      std::remove_if(_atoms.begin(), _atoms.end(), NotLive(_liveAtoms)),
      _atoms.end());
}

// error out if some undefines remain
bool Resolver::checkUndefines(bool final) {
  // when using LTO, undefines are checked after bitcode is optimized
  if (_haveLLVMObjs && !final)
    return false;

  // build vector of remaining undefined symbols
  std::vector<const UndefinedAtom *> undefinedAtoms;
  _symbolTable.undefines(undefinedAtoms);
  if (_context.deadStrip()) {
    // When dead code stripping, we don't care if dead atoms are undefined.
    undefinedAtoms.erase(std::remove_if(undefinedAtoms.begin(),
                                        undefinedAtoms.end(),
                                        NotLive(_liveAtoms)),
                         undefinedAtoms.end());
  }

  // error message about missing symbols
  if (!undefinedAtoms.empty()) {
    // FIXME: need diagnostics interface for writing error messages
    bool foundUndefines = false;
    for (const UndefinedAtom *undefAtom : undefinedAtoms) {
      const File &f = undefAtom->file();

      // Skip over a weak symbol.
      if (undefAtom->canBeNull() != UndefinedAtom::canBeNullNever)
        continue;

      // If this is a library and undefined symbols are allowed on the
      // target platform, skip over it.
      if (isa<SharedLibraryFile>(f) && _context.allowShlibUndefines())
        continue;

      // If the undefine is coalesced away, skip over it.
      if (_symbolTable.replacement(undefAtom) != undefAtom)
        continue;

      // Seems like this symbol is undefined. Warn that.
      foundUndefines = true;
      if (_context.printRemainingUndefines()) {
        llvm::errs() << "Undefined Symbol: " << undefAtom->file().path()
                     << " : " << undefAtom->name() << "\n";
      }
    }
    if (foundUndefines) {
      if (_context.printRemainingUndefines())
        llvm::errs() << "symbol(s) not found\n";
      return true;
    }
  }
  return false;
}

// remove from _atoms all coaleseced away atoms
void Resolver::removeCoalescedAwayAtoms() {
  ScopedTask task(getDefaultDomain(), "removeCoalescedAwayAtoms");
  _atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(),
                              AtomCoalescedAway(_symbolTable)),
               _atoms.end());
}

void Resolver::linkTimeOptimize() {
  // FIX ME
}

bool Resolver::resolve() {
  if (!this->resolveUndefines())
    return false;
  this->updateReferences();
  this->deadStripOptimize();
  if (this->checkUndefines(false))
    if (!_context.allowRemainingUndefines())
      return false;
  this->removeCoalescedAwayAtoms();
  this->linkTimeOptimize();
  this->_result->addAtoms(_atoms);
  return true;
}

void Resolver::MergedFile::addAtom(const Atom &atom) {
  if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(&atom)) {
    _definedAtoms._atoms.push_back(defAtom);
  } else if (const UndefinedAtom *undefAtom = dyn_cast<UndefinedAtom>(&atom)) {
    _undefinedAtoms._atoms.push_back(undefAtom);
  } else if (const SharedLibraryAtom *slAtom =
                 dyn_cast<SharedLibraryAtom>(&atom)) {
    _sharedLibraryAtoms._atoms.push_back(slAtom);
  } else if (const AbsoluteAtom *abAtom = dyn_cast<AbsoluteAtom>(&atom)) {
    _absoluteAtoms._atoms.push_back(abAtom);
  } else {
    llvm_unreachable("atom has unknown definition kind");
  }
}

MutableFile::DefinedAtomRange Resolver::MergedFile::definedAtoms() {
  return range<std::vector<const DefinedAtom *>::iterator>(
      _definedAtoms._atoms.begin(), _definedAtoms._atoms.end());
}

void Resolver::MergedFile::addAtoms(std::vector<const Atom *> &all) {
  ScopedTask task(getDefaultDomain(), "addAtoms");
  DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Resolver final atom list:\n");
  for (const Atom *atom : all) {
    DEBUG_WITH_TYPE("resolver", llvm::dbgs()
                    << llvm::format("    0x%09lX", atom)
                    << ", name="
                    << atom->name()
                    << "\n");
    this->addAtom(*atom);
  }
}

} // namespace lld
<|endoftext|>


<commit_before>#include "Ruby.h"
#include "RubyObject.h"
#include "RubyModule.h"
#include "common.h"
#include <cstring>
#include <string>

using namespace v8;

VALUE Ruby::BLOCK_WRAPPER_CLASS;

void Ruby::Init(Handle<Object> module)
{
  static char* argv[] = { (char*)"norby", (char*)"-e", (char*)"" };

  RUBY_INIT_STACK;
  ruby_init();
  ruby_options(3, argv);

  BLOCK_WRAPPER_CLASS = rb_define_class("BlockWrapper", rb_cObject);

  node::AtExit(Cleanup);

  module->Set(NanNew<String>("exports"),
              NanNew<FunctionTemplate>(New)->GetFunction());
}

void Ruby::Cleanup(void*)
{
  LOG("Cleaning up!");
  RubyObject::Cleanup();
  ruby_cleanup(0);
}

NAN_METHOD(Ruby::New)
{
  NanScope();

  assert(args[0]->IsObject());
  Local<Object> ctors = args[0].As<Object>();

  Local<Function> createCtor =
    ctors->Get(NanNew<String>("createCtor")).As<Function>();
  assert(createCtor->IsFunction());
  RubyModule::SetCreateCtor(createCtor);

  Local<Object> bindings = NanNew<Object>();
  NODE_SET_METHOD(bindings, "_getClass", GetClass);
  NODE_SET_METHOD(bindings, "_defineClass", DefineClass);
  NODE_SET_METHOD(bindings, "getMethod", GetMethod);
  // TODO: Maybe we should load the constants here and place them in an object?
  NODE_SET_METHOD(bindings, "getConstant", GetConstant);

  NanReturnValue(bindings);
}

struct ConstGetter
{
  ConstGetter(Handle<Value> nv) : nameVal(nv) {}
  VALUE operator()() const
  {
    NanScope();

    Local<String> constName = nameVal->ToString();
    String::Utf8Value constStr(constName);

    ID id;
    VALUE mod;
    const char* split = std::strstr(*constStr, "::");
    if (split) {
      id = rb_intern(split + 2);
      mod = rb_const_get(rb_cObject, rb_intern2(*constStr, split - *constStr));
    }
    else {
      id = rb_intern2(*constStr, constStr.length());
      mod = rb_cObject;
    }

    return rb_const_get(mod, id);
  }

  Handle<Value> nameVal;
};

// TODO: Do we still need this and .getConstant?
NAN_METHOD(Ruby::GetClass)
{
  NanScope();

  VALUE klass;
  SAFE_RUBY_CALL(klass, ConstGetter(args[0]));

  // TODO: rb_is_class_id?
  if (TYPE(klass) != T_CLASS) {
    std::string msg(*String::Utf8Value(args[0]));
    msg.append(" is not a class");
    NanThrowTypeError(msg.c_str());
    NanReturnUndefined();
  }

  NanReturnValue(RubyModule::ToV8(klass));
}

struct ClassDefiner
{
  ClassDefiner(Handle<Value> nv, VALUE s) : nameVal(nv), super(s) {}
  VALUE operator()() const
  {
    NanScope();

    Local<String> className = nameVal->ToString();
    return rb_define_class(*String::Utf8Value(className), super);
  }

  Handle<Value> nameVal;
  VALUE super;
};

NAN_METHOD(Ruby::DefineClass)
{
  NanScope();

  LOG("Inherit called for " << *String::Utf8Value(args[0]));

  VALUE super;
  SAFE_RUBY_CALL(super, ConstGetter(args[1]));
  VALUE klass;
  SAFE_RUBY_CALL(klass, ClassDefiner(args[0], super));

  NanReturnValue(RubyModule::ToV8(klass));
}

NAN_METHOD(CallMethod)
{
  NanScope();
  NanReturnValue(CallRubyFromV8(rb_cObject, args));
}

// TODO: Should this throw immediately if the function doesnt exist?
NAN_METHOD(Ruby::GetMethod)
{
  NanScope();

  Local<String> name = args[0]->ToString();
  ID methodID = rb_intern(*String::Utf8Value(name));
  Local<Function> func =
    NanNew<FunctionTemplate>(CallMethod, EXTERNAL_WRAP((void*)methodID))->GetFunction();
  func->SetName(name);

  NanReturnValue(func);
}

NAN_METHOD(Ruby::GetConstant)
{
  NanScope();

  VALUE constant;
  SAFE_RUBY_CALL(constant, ConstGetter(args[0]));

  // TODO: Should we allow getting classes this way? Maybe throw an exception?
  NanReturnValue(rubyToV8(constant));
}
<commit_msg>Not sure how I got away with this for so long...<commit_after>#include "Ruby.h"
#include "RubyObject.h"
#include "RubyModule.h"
#include "common.h"
#include <cstring>
#include <string>

using namespace v8;

VALUE Ruby::BLOCK_WRAPPER_CLASS;

void Ruby::Init(Handle<Object> module)
{
  static char* argv[] = { (char*)"norby", (char*)"-e", (char*)"" };

  RUBY_INIT_STACK;
  ruby_init();
  ruby_options(3, argv);

  BLOCK_WRAPPER_CLASS = rb_define_class("BlockWrapper", rb_cObject);

  node::AtExit(Cleanup);

  RubyObject::Init();
  module->Set(NanNew<String>("exports"),
              NanNew<FunctionTemplate>(New)->GetFunction());
}

void Ruby::Cleanup(void*)
{
  LOG("Cleaning up!");
  RubyObject::Cleanup();
  ruby_cleanup(0);
}

NAN_METHOD(Ruby::New)
{
  NanScope();

  assert(args[0]->IsObject());
  Local<Object> ctors = args[0].As<Object>();

  Local<Function> createCtor =
    ctors->Get(NanNew<String>("createCtor")).As<Function>();
  assert(createCtor->IsFunction());
  RubyModule::SetCreateCtor(createCtor);

  Local<Object> bindings = NanNew<Object>();
  NODE_SET_METHOD(bindings, "_getClass", GetClass);
  NODE_SET_METHOD(bindings, "_defineClass", DefineClass);
  NODE_SET_METHOD(bindings, "getMethod", GetMethod);
  // TODO: Maybe we should load the constants here and place them in an object?
  NODE_SET_METHOD(bindings, "getConstant", GetConstant);

  NanReturnValue(bindings);
}

struct ConstGetter
{
  ConstGetter(Handle<Value> nv) : nameVal(nv) {}
  VALUE operator()() const
  {
    NanScope();

    Local<String> constName = nameVal->ToString();
    String::Utf8Value constStr(constName);

    ID id;
    VALUE mod;
    const char* split = std::strstr(*constStr, "::");
    if (split) {
      id = rb_intern(split + 2);
      mod = rb_const_get(rb_cObject, rb_intern2(*constStr, split - *constStr));
    }
    else {
      id = rb_intern2(*constStr, constStr.length());
      mod = rb_cObject;
    }

    return rb_const_get(mod, id);
  }

  Handle<Value> nameVal;
};

// TODO: Do we still need this and .getConstant?
NAN_METHOD(Ruby::GetClass)
{
  NanScope();

  VALUE klass;
  SAFE_RUBY_CALL(klass, ConstGetter(args[0]));

  // TODO: rb_is_class_id?
  if (TYPE(klass) != T_CLASS) {
    std::string msg(*String::Utf8Value(args[0]));
    msg.append(" is not a class");
    NanThrowTypeError(msg.c_str());
    NanReturnUndefined();
  }

  NanReturnValue(RubyModule::ToV8(klass));
}

struct ClassDefiner
{
  ClassDefiner(Handle<Value> nv, VALUE s) : nameVal(nv), super(s) {}
  VALUE operator()() const
  {
    NanScope();

    Local<String> className = nameVal->ToString();
    return rb_define_class(*String::Utf8Value(className), super);
  }

  Handle<Value> nameVal;
  VALUE super;
};

NAN_METHOD(Ruby::DefineClass)
{
  NanScope();

  LOG("Inherit called for " << *String::Utf8Value(args[0]));

  VALUE super;
  SAFE_RUBY_CALL(super, ConstGetter(args[1]));
  VALUE klass;
  SAFE_RUBY_CALL(klass, ClassDefiner(args[0], super));

  NanReturnValue(RubyModule::ToV8(klass));
}

NAN_METHOD(CallMethod)
{
  NanScope();
  NanReturnValue(CallRubyFromV8(rb_cObject, args));
}

// TODO: Should this throw immediately if the function doesnt exist?
NAN_METHOD(Ruby::GetMethod)
{
  NanScope();

  Local<String> name = args[0]->ToString();
  ID methodID = rb_intern(*String::Utf8Value(name));
  Local<Function> func =
    NanNew<FunctionTemplate>(CallMethod, EXTERNAL_WRAP((void*)methodID))->GetFunction();
  func->SetName(name);

  NanReturnValue(func);
}

NAN_METHOD(Ruby::GetConstant)
{
  NanScope();

  VALUE constant;
  SAFE_RUBY_CALL(constant, ConstGetter(args[0]));

  // TODO: Should we allow getting classes this way? Maybe throw an exception?
  NanReturnValue(rubyToV8(constant));
}
<|endoftext|>


<commit_before>//===- lib/Driver/Driver.cpp - Linker Driver Emulator ---------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lld/Driver/Driver.h"

#include "lld/Core/LLVM.h"
#include "lld/Core/Instrumentation.h"
#include "lld/Core/PassManager.h"
#include "lld/Core/Parallel.h"
#include "lld/Core/Resolver.h"
#include "lld/ReaderWriter/Reader.h"
#include "lld/ReaderWriter/Writer.h"

#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Option/Arg.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"

namespace lld {

/// This is where the link is actually performed.
bool Driver::link(LinkingContext &context, raw_ostream &diagnostics) {
  // Honor -mllvm
  if (!context.llvmOptions().empty()) {
    unsigned numArgs = context.llvmOptions().size();
    const char **args = new const char *[numArgs + 2];
    args[0] = "lld (LLVM option parsing)";
    for (unsigned i = 0; i != numArgs; ++i)
      args[i + 1] = context.llvmOptions()[i];
    args[numArgs + 1] = 0;
    llvm::cl::ParseCommandLineOptions(numArgs + 1, args);
  }
  InputGraph &inputGraph = context.inputGraph();
  if (!inputGraph.size())
    return false;

  bool fail = false;

  // Read inputs
  ScopedTask readTask(getDefaultDomain(), "Read Args");
  TaskGroup tg;
  int index = 0;
  for (auto &ie : inputGraph.inputElements()) {
    tg.spawn([&, index] {
      if (error_code ec = ie->parse(context, diagnostics)) {
        FileNode *fileNode = (llvm::dyn_cast<FileNode>)(ie.get());
        diagnostics << fileNode->errStr(ec) << "\n";
        fail = true;
      }
    });
    ++index;
  }
  tg.sync();
  readTask.end();

  if (fail)
    return false;

  std::unique_ptr<SimpleFileNode> fileNode(
      new SimpleFileNode("Internal Files"));

  InputGraph::FileVectorT internalFiles;
  context.createInternalFiles(internalFiles);

  if (internalFiles.size()) {
    fileNode->appendInputFiles(std::move(internalFiles));
  }

  // Give target a chance to add files.
  InputGraph::FileVectorT implicitFiles;
  context.createImplicitFiles(implicitFiles);
  if (implicitFiles.size()) {
    fileNode->appendInputFiles(std::move(implicitFiles));
  }

  context.inputGraph().insertOneElementAt(std::move(fileNode),
                                          InputGraph::Position::BEGIN);

  context.inputGraph().assignOrdinals();

  context.inputGraph().doPostProcess();

  uint64_t ordinal = 0;
  context.inputGraph().assignFileOrdinals(ordinal);

  // Do core linking.
  ScopedTask resolveTask(getDefaultDomain(), "Resolve");
  Resolver resolver(context);
  if (resolver.resolve()) {
    if (!context.allowRemainingUndefines())
      return false;
  }
  MutableFile &merged = resolver.resultFile();
  resolveTask.end();

  // Run passes on linked atoms.
  ScopedTask passTask(getDefaultDomain(), "Passes");
  PassManager pm;
  context.addPasses(pm);
  pm.runOnFile(merged);
  passTask.end();

  // Give linked atoms to Writer to generate output file.
  ScopedTask writeTask(getDefaultDomain(), "Write");
  if (error_code ec = context.writeFile(merged)) {
    diagnostics << "Failed to write file '" << context.outputPath()
                << "': " << ec.message() << "\n";
    return false;
  }

  return true;
}

} // namespace
<commit_msg>Fix flaky elf/X86_64/dynlib-search.test.<commit_after>//===- lib/Driver/Driver.cpp - Linker Driver Emulator ---------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lld/Driver/Driver.h"

#include "lld/Core/LLVM.h"
#include "lld/Core/Instrumentation.h"
#include "lld/Core/PassManager.h"
#include "lld/Core/Parallel.h"
#include "lld/Core/Resolver.h"
#include "lld/ReaderWriter/Reader.h"
#include "lld/ReaderWriter/Writer.h"

#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Option/Arg.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"

#include <mutex>

namespace lld {

/// This is where the link is actually performed.
bool Driver::link(LinkingContext &context, raw_ostream &diagnostics) {
  // Honor -mllvm
  if (!context.llvmOptions().empty()) {
    unsigned numArgs = context.llvmOptions().size();
    const char **args = new const char *[numArgs + 2];
    args[0] = "lld (LLVM option parsing)";
    for (unsigned i = 0; i != numArgs; ++i)
      args[i + 1] = context.llvmOptions()[i];
    args[numArgs + 1] = 0;
    llvm::cl::ParseCommandLineOptions(numArgs + 1, args);
  }
  InputGraph &inputGraph = context.inputGraph();
  if (!inputGraph.size())
    return false;

  bool fail = false;

  // Read inputs
  ScopedTask readTask(getDefaultDomain(), "Read Args");
  TaskGroup tg;
  int index = 0;
  std::mutex diagnosticsMutex;
  for (auto &ie : inputGraph.inputElements()) {
    tg.spawn([&, index] {
      // Writes to the same output stream is not guaranteed to be thread-safe.
      // We buffer the diagnostics output to a separate string-backed output
      // stream, acquire the lock, and then print it out.
      std::string buf;
      llvm::raw_string_ostream stream(buf);

      if (error_code ec = ie->parse(context, stream)) {
        FileNode *fileNode = llvm::dyn_cast<FileNode>(ie.get());
        stream << fileNode->errStr(ec) << "\n";
        fail = true;
      }

      stream.flush();
      if (!buf.empty()) {
        std::lock_guard<std::mutex> lock(diagnosticsMutex);
        diagnostics << buf;
      }
    });
    ++index;
  }
  tg.sync();
  readTask.end();

  if (fail)
    return false;

  std::unique_ptr<SimpleFileNode> fileNode(
      new SimpleFileNode("Internal Files"));

  InputGraph::FileVectorT internalFiles;
  context.createInternalFiles(internalFiles);

  if (internalFiles.size()) {
    fileNode->appendInputFiles(std::move(internalFiles));
  }

  // Give target a chance to add files.
  InputGraph::FileVectorT implicitFiles;
  context.createImplicitFiles(implicitFiles);
  if (implicitFiles.size()) {
    fileNode->appendInputFiles(std::move(implicitFiles));
  }

  context.inputGraph().insertOneElementAt(std::move(fileNode),
                                          InputGraph::Position::BEGIN);

  context.inputGraph().assignOrdinals();

  context.inputGraph().doPostProcess();

  uint64_t ordinal = 0;
  context.inputGraph().assignFileOrdinals(ordinal);

  // Do core linking.
  ScopedTask resolveTask(getDefaultDomain(), "Resolve");
  Resolver resolver(context);
  if (resolver.resolve()) {
    if (!context.allowRemainingUndefines())
      return false;
  }
  MutableFile &merged = resolver.resultFile();
  resolveTask.end();

  // Run passes on linked atoms.
  ScopedTask passTask(getDefaultDomain(), "Passes");
  PassManager pm;
  context.addPasses(pm);
  pm.runOnFile(merged);
  passTask.end();

  // Give linked atoms to Writer to generate output file.
  ScopedTask writeTask(getDefaultDomain(), "Write");
  if (error_code ec = context.writeFile(merged)) {
    diagnostics << "Failed to write file '" << context.outputPath()
                << "': " << ec.message() << "\n";
    return false;
  }

  return true;
}

} // namespace
<|endoftext|>


<commit_before>#include <Time.h>

#include <assert.h>

Time::Time()
	: sec_(0), min_(0), hour_(0), weekday_(0)
	, month_(0), monthday_(0), year_(2000)
{}

void Time::inc()
{
	++sec_;
	if(sec_<60)
		return;
	sec_=0; ++min_;
	if(min_<60)
		return;
	min_=0; ++hour_;
	if(hour_<24)
		return;
	hour_=0;
	++weekday_;
	if(weekday_==7)
		weekday_=0;

	if(monthday_ < maxMonthday())
		return;

	monthday_ = 0; ++month_;
	if(month_<12)
		return;
	month_ = 0;
	if(year_<2099)
		++year_;

	checkInvariant();
}

int Time::maxMonthday() const
{
	return month_ == 1? (isLeapYear()? 28 : 29)
		: ((month_ == 0 || month_ == 2 || month_ == 4 || month_ == 6
			|| month_ == 7 || month_ == 9 || month_ == 11)? 31 : 30);
}

void Time::checkInvariant() const
{
	assert(sec_>=0 && sec_<60);
	assert(min_>=0 && min_<60);
	assert(hour_>=0 && hour_<24);
	assert(weekday_>=0 && weekday_<7);
	assert((month_>=0 && month_<12) || month_ == ANY);
	assert(year_>=2000 && year_<2100);		// accorcding to the watch spec

	if(monthday_ != ANY)
	{
		assert(monthday_>=0);
		if(month_ == 0 || month_ == 2 || month_ == 4 || month_ == 6
				|| month_ == 7 || month_ == 9 || month_ == 11)
			assert(monthday_<31);
		else if(month_ == 1)
			assert(monthday_<(isLeapYear()? 28 : 29));
		else
			assert(monthday_<30);
	}
}

<commit_msg>Set initial weekday to Saturday<commit_after>#include <Time.h>

#include <assert.h>

Time::Time()
	: sec_(0), min_(0), hour_(0), weekday_(6)	// 01.01.2000 was Saturday
	, month_(0), monthday_(0), year_(2000)
{}

void Time::inc()
{
	++sec_;
	if(sec_<60)
		return;
	sec_=0; ++min_;
	if(min_<60)
		return;
	min_=0; ++hour_;
	if(hour_<24)
		return;
	hour_=0;
	++weekday_;
	if(weekday_==7)
		weekday_=0;

	if(monthday_ < maxMonthday())
		return;

	monthday_ = 0; ++month_;
	if(month_<12)
		return;
	month_ = 0;
	if(year_<2099)
		++year_;

	checkInvariant();
}

int Time::maxMonthday() const
{
	return month_ == 1? (isLeapYear()? 28 : 29)
		: ((month_ == 0 || month_ == 2 || month_ == 4 || month_ == 6
			|| month_ == 7 || month_ == 9 || month_ == 11)? 31 : 30);
}

void Time::checkInvariant() const
{
	assert(sec_>=0 && sec_<60);
	assert(min_>=0 && min_<60);
	assert(hour_>=0 && hour_<24);
	assert(weekday_>=0 && weekday_<7);
	assert((month_>=0 && month_<12) || month_ == ANY);
	assert(year_>=2000 && year_<2100);		// accorcding to the watch spec

	if(monthday_ != ANY)
	{
		assert(monthday_>=0);
		if(month_ == 0 || month_ == 2 || month_ == 4 || month_ == 6
				|| month_ == 7 || month_ == 9 || month_ == 11)
			assert(monthday_<31);
		else if(month_ == 1)
			assert(monthday_<(isLeapYear()? 28 : 29));
		else
			assert(monthday_<30);
	}
}

<|endoftext|>


<commit_before>// TODO(carpenter): move this into test/unit/command
//                  it's here now because it won't compile there

#include <gtest/gtest.h>
#include <stan/lang/compiler.hpp>
#include <stan/command/stanc_helper.hpp>
#include <test/unit/util.hpp>
#include <fstream>
#include <sstream>

void expect_find(const std::string& str, const std::string& target) {
  EXPECT_TRUE(str.find(target) != std::string::npos)
      << str << " does not contain " << target << std::endl;
}

TEST(commandStancHelper, printVersion) {
  std::stringstream ss;
  print_version(&ss);
  expect_find(ss.str(), "stanc version 2.");
}

TEST(commandStancHelper, printStancHelp) {
  std::stringstream ss;
  print_stanc_help(&ss);
  expect_find(ss.str(), "USAGE:  stanc [options] <model_file>");
  expect_find(ss.str(), "OPTIONS:");
}

bool create_test_file(const std::string& path, const std::string& program) {
  std::string cmd_setup = "mkdir -p test/test-models";
  int return_code = system(cmd_setup.c_str());
  std::string cmd = "echo ";
  cmd += "\"";
  cmd += program;
  cmd += "\"";
  cmd += " > ";
  cmd += path;
  return_code = system(cmd.c_str());
  return return_code == 0;
}

bool create_test_file() {
  std::string program = "parameters { real y; } model { y ~ normal(0, 1); }";
  return create_test_file("test/test-models/temp-bar.stan", program);
}

TEST(commandStancHelper, deleteFile) {
  if (!create_test_file())
    return;
  std::stringstream ss;
  delete_file(&ss, "test/test-models/temp-bar.stan");
  // test there's no error message
  EXPECT_EQ(0, ss.str().size());
  // and then test the file stream can't be opened
  std::fstream fs;
  fs.open("test/test-models/temp-bar.stan", std::fstream::in);
  EXPECT_FALSE(fs.is_open());
  fs.close();
}

int run_helper(const std::string& path, std::ostream& out, std::ostream& err) {
  int argc = 2;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back(path.c_str());
  const char** argv = &argv_vec[0];
  return stanc_helper(argc, argv, &out, &err);
}

TEST(commandStancHelper, readOnlyOK) {
  std::stringstream out;
  std::stringstream err;
  int rc = run_helper("src/test/test-models/good/stanc_helper.stan", out, err);
  EXPECT_EQ(0, rc) << "out=" << out.str() << std::endl
                   << "err=" << err.str() << std::endl;
  expect_find(out.str(), "Model name=stanc_helper_model");
  expect_find(out.str(),
              "Input file=src/test/test-models/good/stanc_helper.stan");
  expect_find(out.str(), "Output file=stanc_helper_model.cpp");
  delete_file(&err, "stanc_helper_model.cpp");
  EXPECT_EQ(0, err.str().size()) << "error=" << err.str() << std::endl;
}

TEST(commandStancHelper, failRC) {
  std::stringstream out;
  std::stringstream err;
  int rc = run_helper("src/test/test-models/bad/stanc_helper.stan", out, err);

  // TODO(carpenter): This should be -2 but it's -3 so
  // I only tested that it's != 0 to contrast with earlier success
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, noSuchFile) {
  std::stringstream out;
  std::stringstream err;
  int argc = 2;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back("src/test/test-models/good/nosuchfile.stan");
  const char** argv = &argv_vec[0];
  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_GT(err.str().size(), 10) << "error=" << err.str() << std::endl;
  expect_find(err.str(), "Failed to open model file");
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, readOnlyDirReadFile) {
  std::stringstream out;
  std::stringstream err;
  int argc = 4;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back("--name=m1");
  argv_vec.push_back("--o=src/test/test-models/m1.cpp");
  argv_vec.push_back("src/test/test-models/bad/read_only/m1.stan");
  const char** argv = &argv_vec[0];
  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc == 0);
  delete_file(&err, "src/test/test-models/m1.cpp");
  EXPECT_EQ(0, err.str().size()) << "error=" << err.str() << std::endl;
}

TEST(commandStancHelper, readOnlyDirWriteFile) {
  std::stringstream out;
  std::stringstream err;
  int argc = 4;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back("--name=m1");
  argv_vec.push_back("--o=src/test/test-models/read_only/m1.cpp");
  argv_vec.push_back("src/test/test-models/bad/read_only/m1.stan");
  const char** argv = &argv_vec[0];
  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, readOnlyDirBadFile) {
  std::stringstream out;
  std::stringstream err;
  int argc = 2;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back("src/test/test-models/bad/read_only/nosuchfile.stan");
  const char** argv = &argv_vec[0];
  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, includeSinglePathGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back("--include_paths=src/test/test-models/included/");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc == 0);
}

TEST(commandStancHelper, includeMultPathSimpleGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back("--include_paths=foo,src/test/test-models/included/,baz");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc == 0);
}

TEST(commandStancHelper, includeMultPathSingleQuoteGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back(
      "--include_paths='path,with,commas',src/test/test-models/included/");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc == 0);
}

TEST(commandStancHelper, includeMultPathDoubleQuoteGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back(
      "--include_paths=\"path,with,commas\",src/test/test-models/included/");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_EQ(0, rc);
}

TEST(commandStancHelper, includeMultPathEscapedCommaGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back(
      "--include_paths=path\\,with\\,commas,src/test/test-models/included/");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_EQ(0, rc);
}

TEST(commandStancHelper, includeMultPathBad) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back("--include_paths=foo,baz");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, includeNoPathBad) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc != 0);
}
<commit_msg>or the return codes to return false in case of any issue<commit_after>// TODO(carpenter): move this into test/unit/command
//                  it's here now because it won't compile there

#include <gtest/gtest.h>
#include <stan/lang/compiler.hpp>
#include <stan/command/stanc_helper.hpp>
#include <test/unit/util.hpp>
#include <fstream>
#include <sstream>

void expect_find(const std::string& str, const std::string& target) {
  EXPECT_TRUE(str.find(target) != std::string::npos)
      << str << " does not contain " << target << std::endl;
}

TEST(commandStancHelper, printVersion) {
  std::stringstream ss;
  print_version(&ss);
  expect_find(ss.str(), "stanc version 2.");
}

TEST(commandStancHelper, printStancHelp) {
  std::stringstream ss;
  print_stanc_help(&ss);
  expect_find(ss.str(), "USAGE:  stanc [options] <model_file>");
  expect_find(ss.str(), "OPTIONS:");
}

bool create_test_file(const std::string& path, const std::string& program) {
  std::string cmd_setup = "mkdir -p test/test-models";
  int return_code = system(cmd_setup.c_str());
  std::string cmd = "echo ";
  cmd += "\"";
  cmd += program;
  cmd += "\"";
  cmd += " > ";
  cmd += path;
  return_code |= system(cmd.c_str());
  return return_code == 0;
}

bool create_test_file() {
  std::string program = "parameters { real y; } model { y ~ normal(0, 1); }";
  return create_test_file("test/test-models/temp-bar.stan", program);
}

TEST(commandStancHelper, deleteFile) {
  if (!create_test_file())
    return;
  std::stringstream ss;
  delete_file(&ss, "test/test-models/temp-bar.stan");
  // test there's no error message
  EXPECT_EQ(0, ss.str().size());
  // and then test the file stream can't be opened
  std::fstream fs;
  fs.open("test/test-models/temp-bar.stan", std::fstream::in);
  EXPECT_FALSE(fs.is_open());
  fs.close();
}

int run_helper(const std::string& path, std::ostream& out, std::ostream& err) {
  int argc = 2;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back(path.c_str());
  const char** argv = &argv_vec[0];
  return stanc_helper(argc, argv, &out, &err);
}

TEST(commandStancHelper, readOnlyOK) {
  std::stringstream out;
  std::stringstream err;
  int rc = run_helper("src/test/test-models/good/stanc_helper.stan", out, err);
  EXPECT_EQ(0, rc) << "out=" << out.str() << std::endl
                   << "err=" << err.str() << std::endl;
  expect_find(out.str(), "Model name=stanc_helper_model");
  expect_find(out.str(),
              "Input file=src/test/test-models/good/stanc_helper.stan");
  expect_find(out.str(), "Output file=stanc_helper_model.cpp");
  delete_file(&err, "stanc_helper_model.cpp");
  EXPECT_EQ(0, err.str().size()) << "error=" << err.str() << std::endl;
}

TEST(commandStancHelper, failRC) {
  std::stringstream out;
  std::stringstream err;
  int rc = run_helper("src/test/test-models/bad/stanc_helper.stan", out, err);

  // TODO(carpenter): This should be -2 but it's -3 so
  // I only tested that it's != 0 to contrast with earlier success
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, noSuchFile) {
  std::stringstream out;
  std::stringstream err;
  int argc = 2;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back("src/test/test-models/good/nosuchfile.stan");
  const char** argv = &argv_vec[0];
  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_GT(err.str().size(), 10) << "error=" << err.str() << std::endl;
  expect_find(err.str(), "Failed to open model file");
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, readOnlyDirReadFile) {
  std::stringstream out;
  std::stringstream err;
  int argc = 4;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back("--name=m1");
  argv_vec.push_back("--o=src/test/test-models/m1.cpp");
  argv_vec.push_back("src/test/test-models/bad/read_only/m1.stan");
  const char** argv = &argv_vec[0];
  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc == 0);
  delete_file(&err, "src/test/test-models/m1.cpp");
  EXPECT_EQ(0, err.str().size()) << "error=" << err.str() << std::endl;
}

TEST(commandStancHelper, readOnlyDirWriteFile) {
  std::stringstream out;
  std::stringstream err;
  int argc = 4;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back("--name=m1");
  argv_vec.push_back("--o=src/test/test-models/read_only/m1.cpp");
  argv_vec.push_back("src/test/test-models/bad/read_only/m1.stan");
  const char** argv = &argv_vec[0];
  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, readOnlyDirBadFile) {
  std::stringstream out;
  std::stringstream err;
  int argc = 2;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back("src/test/test-models/bad/read_only/nosuchfile.stan");
  const char** argv = &argv_vec[0];
  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, includeSinglePathGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back("--include_paths=src/test/test-models/included/");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc == 0);
}

TEST(commandStancHelper, includeMultPathSimpleGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back("--include_paths=foo,src/test/test-models/included/,baz");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc == 0);
}

TEST(commandStancHelper, includeMultPathSingleQuoteGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back(
      "--include_paths='path,with,commas',src/test/test-models/included/");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc == 0);
}

TEST(commandStancHelper, includeMultPathDoubleQuoteGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back(
      "--include_paths=\"path,with,commas\",src/test/test-models/included/");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_EQ(0, rc);
}

TEST(commandStancHelper, includeMultPathEscapedCommaGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back(
      "--include_paths=path\\,with\\,commas,src/test/test-models/included/");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_EQ(0, rc);
}

TEST(commandStancHelper, includeMultPathBad) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back("--include_paths=foo,baz");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, includeNoPathBad) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc != 0);
}
<|endoftext|>


<commit_before>#include <stan/optimization/bfgs.hpp>
#include <gtest/gtest.h>

TEST(OptimizationBFGS, cubic_interp) {
  using stan::optimization::CubicInterp;
  static const unsigned int nVals = 5;
  static const double xVals[5] = { -2.0, -1.0, 0.0, 1.0, 2.0 };
  double xMin;

  for (unsigned int i = 0; i < nVals; i++) {
    const double &x0 = xVals[i];
    const double f0 = x0*x0*x0/3.0 - x0;
    const double df0 = x0*x0 - 1.0;
    for (unsigned int j = 0; j < nVals; j++) {
      if (i == j)
        continue;

      const double &x1 = xVals[j];
      const double f1 = x1*x1*x1/3.0 - x1;
      const double df1 = x1*x1 - 1.0;

      xMin = CubicInterp(x0,f0,df0,x1,f1,df1,-3.0,2.0);
      EXPECT_NEAR(-3.0,xMin,1e-8);

      xMin = CubicInterp(x0,f0,df0,x1,f1,df1,-3.0,0.0);
      EXPECT_NEAR(-3.0,xMin,1e-8);

      xMin = CubicInterp(x0,f0,df0,x1,f1,df1,-1.0,2.0);
      EXPECT_NEAR(1.0,xMin,1e-8);

      xMin = CubicInterp(x0,f0,df0,x1,f1,df1,0.0,2.0);
      EXPECT_NEAR(1.0,xMin,1e-8);

      xMin = CubicInterp(x0,f0,df0,x1,f1,df1,0.5,1.5);
      EXPECT_NEAR(1.0,xMin,1e-8);
    }
  }
}

class linesearch_testfunc {
public:
  int operator()(const Eigen::Matrix<double,Eigen::Dynamic,1> &x,
                 double &f, Eigen::Matrix<double,Eigen::Dynamic,1> &g) {
    f = x.dot(x) - 1.0;
    g = 2.0*x;
    return 0;
  }
};

TEST(OptimizationBFGS, wolfe_line_search) {
  using stan::optimization::WolfeLineSearch;

  static const double c1 = 1e-4;
  static const double c2 = 0.9;
  static const double minAlpha = 1e-16;

  linesearch_testfunc func1;
  Eigen::Matrix<double,-1,1> x0,x1;
  double f0,f1;
  Eigen::Matrix<double,-1,1> p, gradx0,gradx1;
  double alpha;
  int ret;

  x0.setOnes(5,1);
  func1(x0,f0,gradx0);

  p = -gradx0;

  alpha = 2.0;
  ret = WolfeLineSearch(func1, alpha,
                        x1, f1, gradx1,
                        p, x0, f0, gradx0,
                        c1, c2, minAlpha);
  EXPECT_NEAR(0.5,alpha,1e-8);

  alpha = 10.0;
  ret = WolfeLineSearch(func1, alpha,
                        x1, f1, gradx1,
                        p, x0, f0, gradx0,
                        c1, c2, minAlpha);
  EXPECT_NEAR(0.5,alpha,1e-8);

  alpha = 0.25;
  ret = WolfeLineSearch(func1, alpha,
                        x1, f1, gradx1,
                        p, x0, f0, gradx0,
                        c1, c2, minAlpha);
  EXPECT_NEAR(0.25,alpha,1e-8);
}

<commit_msg>Check return code of WolfeLineSearch in unit test.<commit_after>#include <stan/optimization/bfgs.hpp>
#include <gtest/gtest.h>

TEST(OptimizationBFGS, cubic_interp) {
  using stan::optimization::CubicInterp;
  static const unsigned int nVals = 5;
  static const double xVals[5] = { -2.0, -1.0, 0.0, 1.0, 2.0 };
  double xMin;

  for (unsigned int i = 0; i < nVals; i++) {
    const double &x0 = xVals[i];
    const double f0 = x0*x0*x0/3.0 - x0;
    const double df0 = x0*x0 - 1.0;
    for (unsigned int j = 0; j < nVals; j++) {
      if (i == j)
        continue;

      const double &x1 = xVals[j];
      const double f1 = x1*x1*x1/3.0 - x1;
      const double df1 = x1*x1 - 1.0;

      xMin = CubicInterp(x0,f0,df0,x1,f1,df1,-3.0,2.0);
      EXPECT_NEAR(-3.0,xMin,1e-8);

      xMin = CubicInterp(x0,f0,df0,x1,f1,df1,-3.0,0.0);
      EXPECT_NEAR(-3.0,xMin,1e-8);

      xMin = CubicInterp(x0,f0,df0,x1,f1,df1,-1.0,2.0);
      EXPECT_NEAR(1.0,xMin,1e-8);

      xMin = CubicInterp(x0,f0,df0,x1,f1,df1,0.0,2.0);
      EXPECT_NEAR(1.0,xMin,1e-8);

      xMin = CubicInterp(x0,f0,df0,x1,f1,df1,0.5,1.5);
      EXPECT_NEAR(1.0,xMin,1e-8);
    }
  }
}

class linesearch_testfunc {
public:
  int operator()(const Eigen::Matrix<double,Eigen::Dynamic,1> &x,
                 double &f, Eigen::Matrix<double,Eigen::Dynamic,1> &g) {
    f = x.dot(x) - 1.0;
    g = 2.0*x;
    return 0;
  }
};

TEST(OptimizationBFGS, wolfe_line_search) {
  using stan::optimization::WolfeLineSearch;

  static const double c1 = 1e-4;
  static const double c2 = 0.9;
  static const double minAlpha = 1e-16;

  linesearch_testfunc func1;
  Eigen::Matrix<double,-1,1> x0,x1;
  double f0,f1;
  Eigen::Matrix<double,-1,1> p, gradx0,gradx1;
  double alpha;
  int ret;

  x0.setOnes(5,1);
  func1(x0,f0,gradx0);

  p = -gradx0;

  alpha = 2.0;
  ret = WolfeLineSearch(func1, alpha,
                        x1, f1, gradx1,
                        p, x0, f0, gradx0,
                        c1, c2, minAlpha);
  EXPECT_EQ(0,ret);
  EXPECT_NEAR(0.5,alpha,1e-8);

  alpha = 10.0;
  ret = WolfeLineSearch(func1, alpha,
                        x1, f1, gradx1,
                        p, x0, f0, gradx0,
                        c1, c2, minAlpha);
  EXPECT_EQ(0,ret);
  EXPECT_NEAR(0.5,alpha,1e-8);

  alpha = 0.25;
  ret = WolfeLineSearch(func1, alpha,
                        x1, f1, gradx1,
                        p, x0, f0, gradx0,
                        c1, c2, minAlpha);
  EXPECT_EQ(0,ret);
  EXPECT_NEAR(0.25,alpha,1e-8);
}

<|endoftext|>


<commit_before>#include <blackhole/formatter/string.hpp>
#include <blackhole/formatter/json.hpp>
#include <blackhole/frontend.hpp>
#include <blackhole/frontend/files.hpp>
#include <blackhole/frontend/syslog.hpp>
#include <blackhole/repository.hpp>
#include <blackhole/sink/files.hpp>
#include <blackhole/sink/socket.hpp>
#include <blackhole/sink/stream.hpp>
#include <blackhole/sink/syslog.hpp>

#include "global.hpp"

using namespace blackhole;

namespace blackhole { namespace sink {

template<>
struct priority_traits<testing::level> {
    static inline
    priority_t map(testing::level lvl) {
        switch (lvl) {
        case testing::level::debug:
            return priority_t::debug;
        case testing::level::info:
            return priority_t::info;
        case testing::level::warn:
            return priority_t::warning;
        case testing::level::error:
            return priority_t::err;
        }

        return priority_t::debug;
    }
};

} } // namespace blackhole::sink

TEST(Factory, StreamStringsFrontend) {
    external_factory_t factory;
    factory.add<sink::stream_t, formatter::string_t>();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("stream");
    sink["output"] = "stdout";

    auto result = factory.create(formatter, sink);
    auto casted = dynamic_cast<
        frontend_t<formatter::string_t, sink::stream_t>*
    >(result.get());
    EXPECT_TRUE(casted != nullptr);
}

TEST(Factory, FileStringsFrontend) {
    external_factory_t factory;
    factory.add<sink::files_t<>, formatter::string_t>();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;

    auto result = factory.create(formatter, sink);
    auto casted = dynamic_cast<
        frontend_t<formatter::string_t, sink::files_t<>>*
    >(result.get());
    EXPECT_TRUE(casted != nullptr);
}

TEST(Repository, RotationFileStringsFrontendWithMoveWatcher) {
    external_factory_t factory;
    factory.add<
        sink::files_t<
            sink::files::boost_backend_t,
            sink::rotator_t<
                sink::files::boost_backend_t,
                sink::rotation::watcher::move_t
            >
        >,
        formatter::string_t
    >();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;
    sink["rotation"]["move"] = true;

    EXPECT_TRUE(bool(factory.create(formatter, sink)));
}

//!@todo: Not truly test.
TEST(Repository, _) {
    external_factory_t factory;
    factory.add<
        sink::files_t<
            sink::files::boost_backend_t,
            sink::rotator_t<
                sink::files::boost_backend_t,
                sink::rotation::watcher::move_t
            >
        >,
        formatter::string_t
    >();

    factory.add<
        sink::files_t<
            sink::files::boost_backend_t,
            sink::rotator_t<
                sink::files::boost_backend_t,
                sink::rotation::watcher::size_t
            >
        >,
        formatter::string_t
    >();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;
    sink["rotation"]["move"] = true;

    EXPECT_TRUE(bool(factory.create(formatter, sink)));

    sink = sink_config_t("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;
    sink["rotation"]["pattern"] = "%(filename)s.log.%N";
    sink["rotation"]["backups"] = 5;
    sink["rotation"]["size"] = 10 * 1024 * 1024;

    EXPECT_TRUE(bool(factory.create(formatter, sink)));
}

TEST(Repository, RotationFileStringsFrontendWithSizeWatcher) {
    external_factory_t factory;
    factory.add<
        sink::files_t<
            sink::files::boost_backend_t,
            sink::rotator_t<
                sink::files::boost_backend_t,
                sink::rotation::watcher::size_t
            >
        >,
        formatter::string_t
    >();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;
    sink["rotation"]["pattern"] = "%(filename)s.log.%N";
    sink["rotation"]["backups"] = 5;
    sink["rotation"]["size"] = 10 * 1024 * 1024;

    EXPECT_TRUE(bool(factory.create(formatter, sink)));
}

TEST(Repository, RotationFileStringsFrontendWithDatetimeWatcher) {
    external_factory_t factory;
    factory.add<
        sink::files_t<
            sink::files::boost_backend_t,
            sink::rotator_t<
                sink::files::boost_backend_t,
                sink::rotation::watcher::datetime_t<>
            >
        >,
        formatter::string_t
    >();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;
    sink["rotation"]["pattern"] = "%(filename)s.log.%N";
    sink["rotation"]["backups"] = 5;
    sink["rotation"]["period"] = "d";

    EXPECT_TRUE(bool(factory.create(formatter, sink)));
}

TEST(Repository, ThrowsExceptionIfRotationWatcherNotSpecified) {
    external_factory_t factory;
    factory.add<
        sink::files_t<
            sink::files::boost_backend_t,
            sink::rotator_t<
                sink::files::boost_backend_t,
                sink::rotation::watcher::datetime_t<>
            >
        >,
        formatter::string_t
    >();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;
    sink["rotation"]["pattern"] = "%(filename)s.log.%N";
    sink["rotation"]["backups"] = 5;

    EXPECT_THROW(factory.create(formatter, sink), blackhole::error_t);
}

TEST(Factory, SyslogStringsFrontend) {
    external_factory_t factory;
    factory.add<sink::syslog_t<level>, formatter::string_t>();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("syslog");
    sink["identity"] = "AppIdentity";

    EXPECT_TRUE(bool(factory.create(formatter, sink)));
}

TEST(Factory, UdpSocketStringsFrontend) {
    external_factory_t factory;
    factory.add<sink::socket_t<boost::asio::ip::udp>, formatter::string_t>();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("udp");
    sink["host"] = "localhost";
    sink["port"] = 50030;

    EXPECT_TRUE(bool(factory.create(formatter, sink)));
}

TEST(Factory, TcpSocketStringsFrontend) {
    external_factory_t factory;
    factory.add<sink::socket_t<boost::asio::ip::tcp>, formatter::string_t>();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    // Sink will try to connect to the specified port. So if it isn't available,
    // an exception will be thrown, it's ok.
    sink_config_t sink("tcp");
    sink["host"] = "localhost";
    sink["port"] = 22;

    EXPECT_TRUE(bool(factory.create(formatter, sink)));
}

log_config_t create_valid_config() {
    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("stream");
    sink["output"] = "stdout";

    frontend_config_t frontend = { formatter, sink };
    return log_config_t{ "root", { frontend } };
}

TEST(Repository, InitializationFromSettings) {
    repository_t repository;
    repository.add_config(create_valid_config());

    const bool condition = std::is_same<
        verbose_logger_t<testing::level>,
        decltype(repository.create<verbose_logger_t<testing::level>>("root"))
    >::value;
    static_assert(condition, "repository should return `verbose_logger_t` object");
}

TEST(Repository, ThrowsExceptionIfLoggerWithSpecifiedNameNotFound) {
    repository_t repository;
    repository.add_config(create_valid_config());
    EXPECT_THROW(repository.create<verbose_logger_t<testing::level>>("log"),
                 std::out_of_range);
}

TEST(Repository, CreatesDuplicateOfRootLoggerByDefault) {
    log_config_t config = create_valid_config();
    repository_t::instance().add_config(config);
    repository_t::instance().create<verbose_logger_t<testing::level>>("root");
}

TEST(Factory, ThrowsExceptionWhenRequestNotRegisteredSink) {
    external_factory_t factory;

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;

    EXPECT_THROW(factory.create(formatter, sink), blackhole::error_t);
}

TEST(Factory, ThrowsExceptionWhenRequestNotRegisteredFormatter) {
    external_factory_t factory;
    factory.add<sink::socket_t<boost::asio::ip::udp>, boost::mpl::list<>>();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;

    EXPECT_THROW(factory.create(formatter, sink), blackhole::error_t);
}

TEST(Repository, StreamSinkWithStringFormatterIsAvailableByDefault) {
    repository_t repository;
    bool registered = repository.registered<
        sink::stream_t,
        formatter::string_t
    >();
    EXPECT_TRUE(registered);
}

TEST(Repository, PairConfiguring) {
    bool registered = false;
    repository_t repository;

    registered = repository.registered<
        sink::syslog_t<level>,
        formatter::string_t
    >();
    EXPECT_FALSE(registered);

    repository.registrate<sink::syslog_t<level>, formatter::string_t>();

    registered = repository.registered<
        sink::syslog_t<level>,
        formatter::string_t
    >();
    EXPECT_TRUE(registered);
}

TEST(Repository, ConfiguringWithMultipleFormatters) {
    typedef boost::mpl::list<
        formatter::string_t,
        formatter::json_t
    > formatters_t;

    bool registered = false;
    repository_t repository;

    registered = repository.registered<sink::files_t<>, formatter::json_t>();
    EXPECT_FALSE(registered);

    repository.registrate<sink::files_t<>, formatters_t>();

    registered = repository.registered<sink::files_t<>, formatter::json_t>();
    EXPECT_TRUE(registered);
}

TEST(Repository, CombinationConfiguring) {
    typedef boost::mpl::vector<
        sink::files_t<>,
        sink::syslog_t<level>
    > sinks_t;

    typedef boost::mpl::list<
        formatter::string_t,
        formatter::json_t
    > formatters_t;

    bool registered = false;
    repository_t repository;

    registered = repository.registered<sink::files_t<>, formatter::json_t>();
    ASSERT_FALSE(registered);
    registered = repository.registered<sink::syslog_t<level>, formatter::string_t>();
    ASSERT_FALSE(registered);
    registered = repository.registered<sink::syslog_t<level>, formatter::json_t>();
    ASSERT_FALSE(registered);

    repository.registrate<sinks_t, formatters_t>();

    registered = repository.registered<sink::files_t<>, formatter::json_t>();
    EXPECT_TRUE(registered);
    registered = repository.registered<sink::syslog_t<level>, formatter::string_t>();
    EXPECT_TRUE(registered);
    registered = repository.registered<sink::syslog_t<level>, formatter::json_t>();
    EXPECT_TRUE(registered);
}

TEST(Repository, ThrowsExceptionWhenSinkIsRegisteredButItsUniqueNameIsDifferent) {
    repository_t repository;

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;
    sink["rotation"]["size"] = 1000;

    frontend_config_t frontend = { formatter, sink };
    log_config_t config = { "root", { frontend } };

    repository.registrate<
        sink::files_t<
            sink::files::boost_backend_t,
            sink::rotator_t<
                sink::files::boost_backend_t,
                sink::rotation::watcher::size_t
            >
        >,
        formatter::string_t
    >();
    repository.add_config(config);

    EXPECT_THROW(repository.create<verbose_logger_t<level>>("root"),
                 blackhole::error_t);
}
<commit_msg>[Unit Testing] I really like result type checks.<commit_after>#include <blackhole/formatter/string.hpp>
#include <blackhole/formatter/json.hpp>
#include <blackhole/frontend.hpp>
#include <blackhole/frontend/files.hpp>
#include <blackhole/frontend/syslog.hpp>
#include <blackhole/repository.hpp>
#include <blackhole/sink/files.hpp>
#include <blackhole/sink/socket.hpp>
#include <blackhole/sink/stream.hpp>
#include <blackhole/sink/syslog.hpp>

#include "global.hpp"

using namespace blackhole;

namespace blackhole { namespace sink {

template<>
struct priority_traits<testing::level> {
    static inline
    priority_t map(testing::level lvl) {
        switch (lvl) {
        case testing::level::debug:
            return priority_t::debug;
        case testing::level::info:
            return priority_t::info;
        case testing::level::warn:
            return priority_t::warning;
        case testing::level::error:
            return priority_t::err;
        }

        return priority_t::debug;
    }
};

} } // namespace blackhole::sink

TEST(Factory, StreamStringsFrontend) {
    external_factory_t factory;
    factory.add<sink::stream_t, formatter::string_t>();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("stream");
    sink["output"] = "stdout";

    auto result = factory.create(formatter, sink);
    auto casted = dynamic_cast<
        frontend_t<formatter::string_t, sink::stream_t>*
    >(result.get());
    EXPECT_TRUE(casted != nullptr);
}

TEST(Factory, FileStringsFrontend) {
    external_factory_t factory;
    factory.add<sink::files_t<>, formatter::string_t>();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;

    auto result = factory.create(formatter, sink);
    auto casted = dynamic_cast<
        frontend_t<formatter::string_t, sink::files_t<>>*
    >(result.get());
    EXPECT_TRUE(casted != nullptr);
}

TEST(Repository, RotationFileStringsFrontendWithMoveWatcher) {
    external_factory_t factory;
    factory.add<
        sink::files_t<
            sink::files::boost_backend_t,
            sink::rotator_t<
                sink::files::boost_backend_t,
                sink::rotation::watcher::move_t
            >
        >,
        formatter::string_t
    >();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;
    sink["rotation"]["move"] = true;

    auto result = factory.create(formatter, sink);
    auto casted = dynamic_cast<
        frontend_t<
            formatter::string_t,
            sink::files_t<
                sink::files::boost_backend_t,
                sink::rotator_t<
                    sink::files::boost_backend_t,
                    sink::rotation::watcher::move_t
                >
            >
        >*
    >(result.get());
    EXPECT_TRUE(casted != nullptr);
}

//!@todo: Not truly test.
TEST(Repository, _) {
    external_factory_t factory;
    factory.add<
        sink::files_t<
            sink::files::boost_backend_t,
            sink::rotator_t<
                sink::files::boost_backend_t,
                sink::rotation::watcher::move_t
            >
        >,
        formatter::string_t
    >();

    factory.add<
        sink::files_t<
            sink::files::boost_backend_t,
            sink::rotator_t<
                sink::files::boost_backend_t,
                sink::rotation::watcher::size_t
            >
        >,
        formatter::string_t
    >();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;
    sink["rotation"]["move"] = true;

    EXPECT_TRUE(bool(factory.create(formatter, sink)));

    sink = sink_config_t("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;
    sink["rotation"]["pattern"] = "%(filename)s.log.%N";
    sink["rotation"]["backups"] = 5;
    sink["rotation"]["size"] = 10 * 1024 * 1024;

    EXPECT_TRUE(bool(factory.create(formatter, sink)));
}

TEST(Repository, RotationFileStringsFrontendWithSizeWatcher) {
    external_factory_t factory;
    factory.add<
        sink::files_t<
            sink::files::boost_backend_t,
            sink::rotator_t<
                sink::files::boost_backend_t,
                sink::rotation::watcher::size_t
            >
        >,
        formatter::string_t
    >();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;
    sink["rotation"]["pattern"] = "%(filename)s.log.%N";
    sink["rotation"]["backups"] = 5;
    sink["rotation"]["size"] = 10 * 1024 * 1024;

    EXPECT_TRUE(bool(factory.create(formatter, sink)));
}

TEST(Repository, RotationFileStringsFrontendWithDatetimeWatcher) {
    external_factory_t factory;
    factory.add<
        sink::files_t<
            sink::files::boost_backend_t,
            sink::rotator_t<
                sink::files::boost_backend_t,
                sink::rotation::watcher::datetime_t<>
            >
        >,
        formatter::string_t
    >();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;
    sink["rotation"]["pattern"] = "%(filename)s.log.%N";
    sink["rotation"]["backups"] = 5;
    sink["rotation"]["period"] = "d";

    EXPECT_TRUE(bool(factory.create(formatter, sink)));
}

TEST(Repository, ThrowsExceptionIfRotationWatcherNotSpecified) {
    external_factory_t factory;
    factory.add<
        sink::files_t<
            sink::files::boost_backend_t,
            sink::rotator_t<
                sink::files::boost_backend_t,
                sink::rotation::watcher::datetime_t<>
            >
        >,
        formatter::string_t
    >();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;
    sink["rotation"]["pattern"] = "%(filename)s.log.%N";
    sink["rotation"]["backups"] = 5;

    EXPECT_THROW(factory.create(formatter, sink), blackhole::error_t);
}

TEST(Factory, SyslogStringsFrontend) {
    external_factory_t factory;
    factory.add<sink::syslog_t<level>, formatter::string_t>();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("syslog");
    sink["identity"] = "AppIdentity";

    EXPECT_TRUE(bool(factory.create(formatter, sink)));
}

TEST(Factory, UdpSocketStringsFrontend) {
    external_factory_t factory;
    factory.add<sink::socket_t<boost::asio::ip::udp>, formatter::string_t>();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("udp");
    sink["host"] = "localhost";
    sink["port"] = 50030;

    EXPECT_TRUE(bool(factory.create(formatter, sink)));
}

TEST(Factory, TcpSocketStringsFrontend) {
    external_factory_t factory;
    factory.add<sink::socket_t<boost::asio::ip::tcp>, formatter::string_t>();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    // Sink will try to connect to the specified port. So if it isn't available,
    // an exception will be thrown, it's ok.
    sink_config_t sink("tcp");
    sink["host"] = "localhost";
    sink["port"] = 22;

    EXPECT_TRUE(bool(factory.create(formatter, sink)));
}

log_config_t create_valid_config() {
    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("stream");
    sink["output"] = "stdout";

    frontend_config_t frontend = { formatter, sink };
    return log_config_t{ "root", { frontend } };
}

TEST(Repository, InitializationFromSettings) {
    repository_t repository;
    repository.add_config(create_valid_config());

    const bool condition = std::is_same<
        verbose_logger_t<testing::level>,
        decltype(repository.create<verbose_logger_t<testing::level>>("root"))
    >::value;
    static_assert(condition, "repository should return `verbose_logger_t` object");
}

TEST(Repository, ThrowsExceptionIfLoggerWithSpecifiedNameNotFound) {
    repository_t repository;
    repository.add_config(create_valid_config());
    EXPECT_THROW(repository.create<verbose_logger_t<testing::level>>("log"),
                 std::out_of_range);
}

TEST(Repository, CreatesDuplicateOfRootLoggerByDefault) {
    log_config_t config = create_valid_config();
    repository_t::instance().add_config(config);
    repository_t::instance().create<verbose_logger_t<testing::level>>("root");
}

TEST(Factory, ThrowsExceptionWhenRequestNotRegisteredSink) {
    external_factory_t factory;

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;

    EXPECT_THROW(factory.create(formatter, sink), blackhole::error_t);
}

TEST(Factory, ThrowsExceptionWhenRequestNotRegisteredFormatter) {
    external_factory_t factory;
    factory.add<sink::socket_t<boost::asio::ip::udp>, boost::mpl::list<>>();

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;

    EXPECT_THROW(factory.create(formatter, sink), blackhole::error_t);
}

TEST(Repository, StreamSinkWithStringFormatterIsAvailableByDefault) {
    repository_t repository;
    bool registered = repository.registered<
        sink::stream_t,
        formatter::string_t
    >();
    EXPECT_TRUE(registered);
}

TEST(Repository, PairConfiguring) {
    bool registered = false;
    repository_t repository;

    registered = repository.registered<
        sink::syslog_t<level>,
        formatter::string_t
    >();
    EXPECT_FALSE(registered);

    repository.registrate<sink::syslog_t<level>, formatter::string_t>();

    registered = repository.registered<
        sink::syslog_t<level>,
        formatter::string_t
    >();
    EXPECT_TRUE(registered);
}

TEST(Repository, ConfiguringWithMultipleFormatters) {
    typedef boost::mpl::list<
        formatter::string_t,
        formatter::json_t
    > formatters_t;

    bool registered = false;
    repository_t repository;

    registered = repository.registered<sink::files_t<>, formatter::json_t>();
    EXPECT_FALSE(registered);

    repository.registrate<sink::files_t<>, formatters_t>();

    registered = repository.registered<sink::files_t<>, formatter::json_t>();
    EXPECT_TRUE(registered);
}

TEST(Repository, CombinationConfiguring) {
    typedef boost::mpl::vector<
        sink::files_t<>,
        sink::syslog_t<level>
    > sinks_t;

    typedef boost::mpl::list<
        formatter::string_t,
        formatter::json_t
    > formatters_t;

    bool registered = false;
    repository_t repository;

    registered = repository.registered<sink::files_t<>, formatter::json_t>();
    ASSERT_FALSE(registered);
    registered = repository.registered<sink::syslog_t<level>, formatter::string_t>();
    ASSERT_FALSE(registered);
    registered = repository.registered<sink::syslog_t<level>, formatter::json_t>();
    ASSERT_FALSE(registered);

    repository.registrate<sinks_t, formatters_t>();

    registered = repository.registered<sink::files_t<>, formatter::json_t>();
    EXPECT_TRUE(registered);
    registered = repository.registered<sink::syslog_t<level>, formatter::string_t>();
    EXPECT_TRUE(registered);
    registered = repository.registered<sink::syslog_t<level>, formatter::json_t>();
    EXPECT_TRUE(registered);
}

TEST(Repository, ThrowsExceptionWhenSinkIsRegisteredButItsUniqueNameIsDifferent) {
    repository_t repository;

    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    sink_config_t sink("files");
    sink["path"] = "/dev/stdout";
    sink["autoflush"] = true;
    sink["rotation"]["size"] = 1000;

    frontend_config_t frontend = { formatter, sink };
    log_config_t config = { "root", { frontend } };

    repository.registrate<
        sink::files_t<
            sink::files::boost_backend_t,
            sink::rotator_t<
                sink::files::boost_backend_t,
                sink::rotation::watcher::size_t
            >
        >,
        formatter::string_t
    >();
    repository.add_config(config);

    EXPECT_THROW(repository.create<verbose_logger_t<level>>("root"),
                 blackhole::error_t);
}
<|endoftext|>