/*******************************************************************************
 * Copyright (c) 2010, 2013 Sonatype, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Stuart McCulloch (Sonatype, Inc.) - initial API and implementation
 *******************************************************************************/
package org.eclipse.sisu.inject;

import java.lang.ref.Reference;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

import junit.framework.TestCase;

public class MildValuesTest
    extends TestCase
{
    public void testSoftValues()
    {
        testValues( true );
    }

    public void testWeakValues()
    {
        testValues( false );
    }

    private static void testValues( final boolean soft )
    {
        final Map<String, String> names =
            new MildValues<String, String>( new LinkedHashMap<String, Reference<String>>(), soft );

        String a = new String( "A" ), b = new String( "B" ), c = new String( "C" );

        assertTrue( names.isEmpty() );
        assertEquals( 0, names.size() );

        names.put( "1", a );

        assertFalse( names.isEmpty() );
        assertEquals( 1, names.size() );

        names.put( "2", b );

        assertFalse( names.isEmpty() );
        assertEquals( 2, names.size() );

        names.put( "3", c );

        assertFalse( names.isEmpty() );
        assertEquals( 3, names.size() );

        Iterator<String> itr = names.keySet().iterator();

        assertTrue( itr.hasNext() );
        assertEquals( "1", itr.next() );
        assertEquals( "2", itr.next() );
        itr.remove();
        assertTrue( itr.hasNext() );
        assertEquals( "3", itr.next() );
        assertFalse( itr.hasNext() );

        names.put( "2", b = new String( "b2b" ) );

        itr = names.values().iterator();

        assertEquals( "A", itr.next() );
        assertEquals( "C", itr.next() );
        assertEquals( "b2b", itr.next() );

        try
        {
            itr.next();
            fail( "Expected NoSuchElementException" );
        }
        catch ( final NoSuchElementException e )
        {
        }

        itr = null;
        int size;

        size = names.size();
        c = null; // clear so element can be evicted
        gc( names, size );

        itr = names.values().iterator();

        assertEquals( "A", itr.next() );
        assertEquals( "b2b", itr.next() );
        assertFalse( itr.hasNext() );
        itr = null;

        size = names.size();
        a = null; // clear so element can be evicted
        gc( names, size );

        itr = names.values().iterator();

        assertEquals( "b2b", itr.next() );
        assertFalse( itr.hasNext() );
        itr = null;

        size = names.size();
        b = null; // clear so element can be evicted
        gc( names, size );

        itr = names.values().iterator();

        assertFalse( itr.hasNext() );
    }

    private static int gc( final Map<?, ?> map, final int size )
    {
        /*
         * Keep forcing GC until the collection compacts itself
         */
        int gcCount = 0, hash = 0;
        do
        {
            try
            {
                final List<byte[]> buf = new LinkedList<byte[]>();
                for ( int i = 0; i < 1024 * 1024; i++ )
                {
                    // try to trigger aggressive GC
                    buf.add( new byte[1024 * 1024] );
                }
                hash += buf.hashCode(); // so JIT doesn't optimize this away
            }
            catch ( final OutOfMemoryError e )
            {
                // ignore...
            }

            System.gc();
            gcCount++;
        }
        while ( map.size() == size && gcCount < 1024 );

        return hash;
    }
}
