/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.ignite.internal.processors.cache;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import org.apache.ignite.Ignite;
import org.apache.ignite.cache.CacheAtomicityMode;
import org.apache.ignite.cache.CacheMode;
import org.apache.ignite.cache.PartitionLossPolicy;
import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.cluster.ClusterState;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.configuration.TopologyValidator;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.util.typedef.G;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.junit.Test;

/**
 * Cache validator metrics test.
 */
public class CacheValidatorMetricsTest extends GridCommonAbstractTest implements Serializable {
    /** Cache name 1. */
    private static String CACHE_NAME_1 = "cache1";

    /** Cache name 2. */
    private static String CACHE_NAME_2 = "cache2";

    /** {@inheritDoc} */
    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);

        cfg.setActiveOnStart(false);

        CacheConfiguration cCfg1 = new CacheConfiguration()
            .setName(CACHE_NAME_1)
            .setCacheMode(CacheMode.PARTITIONED)
            .setBackups(0)
            .setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL)
            .setPartitionLossPolicy(PartitionLossPolicy.READ_ONLY_SAFE);

        CacheConfiguration cCfg2 = new CacheConfiguration()
            .setName(CACHE_NAME_2)
            .setCacheMode(CacheMode.REPLICATED)
            .setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL)
            .setTopologyValidator(new TopologyValidator() {
                @Override public boolean validate(Collection<ClusterNode> nodes) {
                    return nodes.size() == 2;
                }
            });

        cfg.setCacheConfiguration(cCfg1, cCfg2);

        return cfg;
    }

    /**
     * Asserts that the cache has appropriate status (indicated by the cache metrics).
     *
     * @param cacheName Cache name.
     * @param validForReading Cache is valid for reading.
     * @param validForWriting Cache is valid for writing.
     */
    void assertCacheStatus(String cacheName, boolean validForReading, boolean validForWriting) {
        List<Ignite> nodes = G.allGrids();

        assertFalse(nodes.isEmpty());

        for (Ignite node : nodes) {
            assertEquals(validForReading, node.cache(cacheName).metrics().isValidForReading());
            assertEquals(validForWriting, node.cache(cacheName).metrics().isValidForWriting());
        }
    }

    /**
     * Test the cache validator metrics.
     * Cache can be invalid for writing due to invalid topology or due to partitions loss.
     * Now we can't reproduce test case with invalid for reading cache. At present, reading from the cache can be
     * invalid only for certain keys or when the cluster is not active (in this case caches are not available at all).
     *
     * @throws Exception If failed.
     */
    @Test
    public void testCacheValidatorMetrics() throws Exception {
        final IgniteEx crd = startGrid(1);
        crd.cluster().state(ClusterState.ACTIVE);
        crd.cluster().baselineAutoAdjustEnabled(false);

        assertCacheStatus(CACHE_NAME_1, true, true);
        assertCacheStatus(CACHE_NAME_2, true, false);

        startGrid(2);
        resetBaselineTopology();

        awaitPartitionMapExchange();

        assertCacheStatus(CACHE_NAME_1, true, true);
        assertCacheStatus(CACHE_NAME_2, true, true);

        stopGrid(1);

        awaitPartitionMapExchange();

        // Invalid for writing due to partitions loss.
        assertCacheStatus(CACHE_NAME_1, false, false);

        // Invalid for writing due to invalid topology.
        assertCacheStatus(CACHE_NAME_2, true, false);
    }
}
