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



package reactor.core.config

import reactor.Environment
import spock.lang.Specification

class PropertiesConfigurationReaderSpec extends Specification {

	def "Default configuration can be read"() {
		given: "a configuration reader"
			def reader = new PropertiesConfigurationReader()

		when: "the default configuration is read"
			def configuration = reader.read()
			def dispatchers = toMapByName configuration.dispatcherConfigurations

		then: "it contains the expected dispatchers"
			configuration.defaultDispatcherName == Environment.SHARED
			dispatchers
			matchesExpectedDefaultConfiguration(dispatchers.dispatcherGroup, DispatcherType.DISPATCHER_GROUP, 0, 2048)
			matchesExpectedDefaultConfiguration(dispatchers.shared, DispatcherType.RING_BUFFER, null, 8192)
			matchesExpectedDefaultConfiguration(dispatchers.workQueue, DispatcherType.WORK_QUEUE, 0, 2048)
			matchesExpectedDefaultConfiguration(dispatchers.threadPoolExecutor, DispatcherType.THREAD_POOL_EXECUTOR, 0, 2048)
	}

	def "Custom default configuration can be read"() {
		given: "a configuration reader"
			def reader = new PropertiesConfigurationReader()

		when: "a custom default configuration is read"
			System.setProperty("reactor.profiles.default", "custom")
			def configuration = reader.read()
			System.clearProperty("reactor.profiles.active")

			def dispatchers = toMapByName configuration.dispatcherConfigurations

		then: "it contains the expected dispatchers"
			dispatchers.size() == 1
			matchesExpectedDefaultConfiguration(dispatchers.alpha, DispatcherType.SYNCHRONOUS, null, null)
	}

	def "Additional profiles can be enabled"() {
		given: "a configuration reader"
			def reader = new PropertiesConfigurationReader()

		when: "an additional profile is active"
			System.setProperty("reactor.profiles.active", "custom")
			def configuration = reader.read()
			System.clearProperty("reactor.profiles.active")

			def dispatchers = toMapByName configuration.dispatcherConfigurations

		then: "it contains the expected dispatchers"
			dispatchers.size() == 5
			matchesExpectedDefaultConfiguration(dispatchers.dispatcherGroup, DispatcherType.DISPATCHER_GROUP, 0, 2048)
			matchesExpectedDefaultConfiguration(dispatchers.threadPoolExecutor, DispatcherType.THREAD_POOL_EXECUTOR, 0, 2048)
			matchesExpectedDefaultConfiguration(dispatchers.workQueue, DispatcherType.WORK_QUEUE, 0, 2048)
			matchesExpectedDefaultConfiguration(dispatchers.alpha, DispatcherType.SYNCHRONOUS, null, null)
	}

	def "An active profile can override a profile that came before it"() {
		given: "a configuration reader"
			def reader = new PropertiesConfigurationReader()

		when: "two active profiles are enabled"
			System.setProperty("reactor.profiles.active", "custom, override-custom")
			def configuration = reader.read()
			System.clearProperty("reactor.profiles.active")

			def dispatchers = toMapByName configuration.dispatcherConfigurations

		then: "the later profile overrides the earlier profile"
			dispatchers.size() == 5
			matchesExpectedDefaultConfiguration(dispatchers.dispatcherGroup, DispatcherType.DISPATCHER_GROUP, 0, 2048)
			matchesExpectedDefaultConfiguration(dispatchers.shared, DispatcherType.RING_BUFFER, null, 8192)
			matchesExpectedDefaultConfiguration(dispatchers.threadPoolExecutor, DispatcherType.THREAD_POOL_EXECUTOR, 0, 2048)
			matchesExpectedDefaultConfiguration(dispatchers.workQueue, DispatcherType.WORK_QUEUE, 0, 2048)
			matchesExpectedDefaultConfiguration(dispatchers.alpha, DispatcherType.RING_BUFFER, null, null)
	}

	def "A active profile can override the default profile"() {
		given: "a configuration reader"
			def reader = new PropertiesConfigurationReader()

		when: "a profile is enabled"
			System.setProperty("reactor.profiles.active", "override-default")
			def configuration = reader.read()
			System.clearProperty("reactor.profiles.active")

			def dispatchers = toMapByName configuration.dispatcherConfigurations

		then: "the active profile overrides the default profile"
			dispatchers.size() == 4
			matchesExpectedDefaultConfiguration(dispatchers.dispatcherGroup, DispatcherType.DISPATCHER_GROUP, 0, 2048)
			matchesExpectedDefaultConfiguration(dispatchers.shared, DispatcherType.RING_BUFFER, null, 512)
			matchesExpectedDefaultConfiguration(dispatchers.workQueue, DispatcherType.WORK_QUEUE, 0, 2048)
			matchesExpectedDefaultConfiguration(dispatchers.threadPoolExecutor, DispatcherType.THREAD_POOL_EXECUTOR, 0, 2048)
	}

	def "A system property can override existing configuration"() {
		given: "a configuration reader"
			def reader = new PropertiesConfigurationReader()

		when: "a profile is enabled and a system property override is set"
			System.setProperty("reactor.profiles.active", "override-default, custom")
			System.setProperty("reactor.dispatchers.alpha.type", "dispatcherGroup")
			def configuration = reader.read()
			System.clearProperty("reactor.profiles.active")
			System.clearProperty("reactor.dispatchers.alpha.type")

			def dispatchers = toMapByName configuration.dispatcherConfigurations

		then: "the system property takes precedence"
			dispatchers.size() == 5
			matchesExpectedDefaultConfiguration(dispatchers.dispatcherGroup, DispatcherType.DISPATCHER_GROUP, 0, 2048)
			matchesExpectedDefaultConfiguration(dispatchers.shared, DispatcherType.RING_BUFFER, null, 512)
			matchesExpectedDefaultConfiguration(dispatchers.workQueue, DispatcherType.WORK_QUEUE, 0, 2048)
			matchesExpectedDefaultConfiguration(dispatchers.threadPoolExecutor, DispatcherType.THREAD_POOL_EXECUTOR, 0, 2048)
			matchesExpectedDefaultConfiguration(dispatchers.alpha, DispatcherType.DISPATCHER_GROUP, null, null)
	}

	def "Missing active profiles are tolerated"() {
		given: "a configuration reader"
			def reader = new PropertiesConfigurationReader()

		when: "a non-existent profile is enabled"
			System.setProperty("reactor.profiles.active", "does-not-exist")
			def configuration = reader.read()
			System.clearProperty("reactor.profiles.active")

			def dispatchers = toMapByName configuration.dispatcherConfigurations

		then: "its absence is tolerated"
			dispatchers.size() == 4
			matchesExpectedDefaultConfiguration(dispatchers.dispatcherGroup, DispatcherType.DISPATCHER_GROUP, 0, 2048)
			matchesExpectedDefaultConfiguration(dispatchers.shared, DispatcherType.RING_BUFFER, null, 8192)
			matchesExpectedDefaultConfiguration(dispatchers.workQueue, DispatcherType.WORK_QUEUE, 0, 2048)
			matchesExpectedDefaultConfiguration(dispatchers.threadPoolExecutor, DispatcherType.THREAD_POOL_EXECUTOR, 0, 2048)
	}

	def "Missing default profile is tolerated"() {
		given: "a configuration reader"
			def reader = new PropertiesConfigurationReader()

		when: "a non-existent default profile is specified"
			System.setProperty("reactor.profiles.default", "does-not-exist")
			System.setProperty("reactor.dispatchers.default", "alpha")
			def configuration = reader.read()
			System.clearProperty("reactor.profiles.default")
			System.clearProperty("reactor.dispatchers.default")

			def dispatchers = toMapByName configuration.dispatcherConfigurations

		then: "its absence is tolerated"
			dispatchers.size() == 0
	}

	def "Dispatchers with unrecognized type are tolerated"() {
		given: "a configuration reader"
			def reader = new PropertiesConfigurationReader()

		when: "a profile containing an unrecognized dispatcher type is enabled"
			System.setProperty("reactor.profiles.active", "unrecognized-type")
			def configuration = reader.read()
			System.clearProperty("reactor.profiles.active")

			def dispatchers = toMapByName configuration.dispatcherConfigurations

		then: "the unrecognized dispatcher type is tolerated"
			dispatchers.size() == 4
			matchesExpectedDefaultConfiguration(dispatchers.dispatcherGroup, DispatcherType.DISPATCHER_GROUP, 0, 2048)
			matchesExpectedDefaultConfiguration(dispatchers.shared, DispatcherType.RING_BUFFER, null, 8192)
			matchesExpectedDefaultConfiguration(dispatchers.workQueue, DispatcherType.WORK_QUEUE, 0, 2048)
			matchesExpectedDefaultConfiguration(dispatchers.threadPoolExecutor, DispatcherType.THREAD_POOL_EXECUTOR, 0, 2048)
	}

	def cleanup() {
		System.clearProperty('reactor.profiles.default')
	}

	def matchesExpectedDefaultConfiguration(dispatcherConfiguration, type, size, backlog) {
		dispatcherConfiguration.type == type &&
				dispatcherConfiguration.size == size &&
				dispatcherConfiguration.backlog == backlog
	}

	def toMapByName(dispatcherConfigurations) {
		def dispatchersByName = [:];
		dispatcherConfigurations.each { configuration -> dispatchersByName[configuration.name] = configuration }
		dispatchersByName
	}

}
