package ioc.specs

import org.apache.tapestry5.ioc.Invokable
import org.apache.tapestry5.ioc.internal.services.PerthreadManagerImpl
import org.apache.tapestry5.ioc.services.ThreadCleanupListener
import org.slf4j.Logger
import spock.lang.Specification

class PerthreadManagerImplSpec extends Specification {

  def "nothing is logged when cleaning up with no listeners"() {
    Logger logger = Mock()

    def manager = new PerthreadManagerImpl(logger)

    when:

    manager.cleanup()

    then:

    0 * _
  }

  def "listeners will only be invoked a single time, then discarded"() {
    Logger logger = Mock()
    ThreadCleanupListener listener = Mock()

    def manager = new PerthreadManagerImpl(logger)

    when:

    manager.addThreadCleanupListener(listener)
    manager.cleanup()

    then:

    1 * listener.threadDidCleanup()
    0 * _

    when:

    manager.cleanup()

    then:

    0 * _
  }

  def "exceptions during thread cleanup are logged and other listeners still invoked"() {
    RuntimeException t = new RuntimeException("Boom!")
    Logger logger = Mock()
    ThreadCleanupListener l1 = Mock()
    ThreadCleanupListener l2 = Mock()

    def manager = new PerthreadManagerImpl(logger)

    manager.addThreadCleanupListener(l1)
    manager.addThreadCleanupListener(l2)

    when:

    manager.cleanup()

    then:

    1 * l1.threadDidCleanup() >> { throw t }
    1 * logger.warn("Error invoking callback {}: {}", {it instanceof Runnable}, t, t)

    then:

    1 * l2.threadDidCleanup()
    0 * _
  }

  def "PerThreadValue does not initially exist"() {
    Logger logger = Mock()
    def manager = new PerthreadManagerImpl(logger)

    when:

    def value = manager.createValue()

    then:

    !value.exists()
    value.get() == null

    when:

    value.set(this)

    then:

    value.exists()
    value.get() == this
  }

  def "PerThreadValue.get() with default returns the default value when the value does not exist"() {
    Logger logger = Mock()
    def manager = new PerthreadManagerImpl(logger)
    def defaultValue = new Object()
    def nonNull = new Object()

    when:

    def value = manager.createValue()

    then:

    value.get(defaultValue).is(defaultValue)

    when:

    value.set(null)

    then:

    value.exists()
    value.get(defaultValue) == null

    when:

    value.set(nonNull)

    then:

    value.get(defaultValue).is(nonNull)
  }

  def "PerthreadManager.run() performs an implicit cleanup"() {
    Logger logger = Mock()
    ThreadCleanupListener listener = Mock()

    def manager = new PerthreadManagerImpl(logger)
    manager.addThreadCleanupListener listener
    def value = manager.createValue()
    def didRun = false

    def runnable = {
      didRun = true
      value.set "bar"
    }

    when:

    manager.run runnable

    then:

    1 * listener.threadDidCleanup()

    didRun
    !value.exists()
  }

  def "PerthreadManager.invoke() performs an implicit cleanup"() {
    Logger logger = Mock()
    ThreadCleanupListener listener = Mock()

    def manager = new PerthreadManagerImpl(logger)
    manager.addThreadCleanupListener listener
    def value = manager.createValue()

    def inv = {
      value.set "bar"
      return "baz"
    } as Invokable

    when:

    assert manager.invoke(inv) == "baz"

    then:

    1 * listener.threadDidCleanup()

    !value.exists()

  }
}
