/*
 * Copyright 2016 the original author or authors.
 *
 * 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 org.gradle.api.internal.cache

import org.gradle.cache.CacheBuilder
import org.gradle.cache.CacheRepository
import org.gradle.cache.PersistentCache
import org.gradle.cache.internal.FileLockManager
import org.gradle.util.GFileUtils

import static org.gradle.cache.internal.filelock.LockOptionsBuilder.mode

import org.gradle.util.GradleVersion

import org.gradle.test.fixtures.file.TestNameTestDirectoryProvider

import org.junit.Rule
import spock.lang.Specification
import spock.lang.Unroll

import static org.gradle.api.internal.cache.GeneratedGradleJarCache.CACHE_DISPLAY_NAME
import static org.gradle.api.internal.cache.GeneratedGradleJarCache.CACHE_KEY

class GeneratedGradleJarCacheTest extends Specification {

    @Rule
    TestNameTestDirectoryProvider tmpDir = new TestNameTestDirectoryProvider()

    def cacheRepository = Mock(CacheRepository)
    def gradleVersion = GradleVersion.current().version
    def cacheBuilder = Mock(CacheBuilder)
    def cache = Mock(PersistentCache)

    def "can close cache"() {
        when:
        def provider = new DefaultGeneratedGradleJarCache(cacheRepository, gradleVersion)
        provider.close()

        then:
        1 * cacheRepository.cache(CACHE_KEY) >> cacheBuilder
        1 * cacheBuilder.withDisplayName(CACHE_DISPLAY_NAME) >> cacheBuilder
        1 * cacheBuilder.withLockOptions(mode(FileLockManager.LockMode.None)) >> cacheBuilder
        1 * cacheBuilder.open() >> { cache }
        1 * cache.close()
    }

    @Unroll
    def "creates JAR file on demand for identifier '#identifier'"(String identifier) {
        def cacheDir = tmpDir.testDirectory
        def jarFile = cacheDir.file("gradle-${identifier}-${gradleVersion}.jar")
        def cacheBuilder = Mock(CacheBuilder)
        def cache = Mock(PersistentCache)

        when:
        def provider = new DefaultGeneratedGradleJarCache(cacheRepository, gradleVersion)
        def resolvedFile = provider.get(identifier) { it.createNewFile() }

        then:
        1 * cacheRepository.cache(CACHE_KEY) >> cacheBuilder
        1 * cacheBuilder.withDisplayName(CACHE_DISPLAY_NAME) >> cacheBuilder
        1 * cacheBuilder.withLockOptions(mode(FileLockManager.LockMode.None)) >> cacheBuilder
        1 * cacheBuilder.open() >> { cache }
        _ * cache.getBaseDir() >> cacheDir
        1 * cache.useCache("Generating $jarFile.name", _)
        jarFile == resolvedFile

        where:
        identifier << ["api", "test-kit"]
    }

    def "reuses existing JAR file if existent"() {
        def cacheDir = tmpDir.testDirectory
        def jarFile = cacheDir.file("gradle-api-${gradleVersion}.jar")
        def cacheBuilder = Mock(CacheBuilder)
        def cache = Mock(PersistentCache)

        when:
        def provider = new DefaultGeneratedGradleJarCache(cacheRepository, gradleVersion)
        def resolvedFile = provider.get("api") { it.createNewFile() }

        then:
        1 * cacheRepository.cache(CACHE_KEY) >> cacheBuilder
        1 * cacheBuilder.withDisplayName(CACHE_DISPLAY_NAME) >> cacheBuilder
        1 * cacheBuilder.withLockOptions(mode(FileLockManager.LockMode.None)) >> cacheBuilder
        1 * cacheBuilder.open() >> { cache }
        _ * cache.getBaseDir() >> cacheDir
        1 * cache.useCache("Generating $jarFile.name", _)
        jarFile == resolvedFile

        when:
        GFileUtils.touch(jarFile)
        resolvedFile = provider.get("api") { Assert.fail("Should not be called if file already exists") }

        then:
        0 * cacheRepository.cache(CACHE_KEY) >> cacheBuilder
        0 * cacheBuilder.withDisplayName(CACHE_DISPLAY_NAME) >> cacheBuilder
        0 * cacheBuilder.withLockOptions(mode(FileLockManager.LockMode.None)) >> cacheBuilder
        0 * cacheBuilder.open() >> { cache }
        _ * cache.getBaseDir() >> cacheDir
        0 * cache.useCache("Generating $jarFile.name", _)
        jarFile == resolvedFile
    }
}
