package io.github.classgraph.issues.issue400;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset;

import java.net.URL;
import java.net.URLClassLoader;

import org.junit.jupiter.api.Test;

import io.github.classgraph.ClassGraph;
import io.github.classgraph.ScanResult;

/**
 * Verify that a large number of stored/deflated nested JAR entries don't cause memory problems.
 * 
 * @author Róbert Papp ( https://github.com/TWiStErRob )
 */
public class Issue400 {

    private static final long MB = 1024 * 1024;
    private static final long MEMORY_TOLERANCE = 2 * MB;

    @SuppressWarnings("null")
    private void loadsJarWithManyNestedEntriesAndDoesNotUseMuchMemory(final URL... jars) {
        final long ramAtStart = usedRam();
        long ramAfterScan;
        try (ScanResult scanResult = new ClassGraph().overrideClassLoaders(new URLClassLoader(jars)).enableAllInfo()
                .scan()) {
            ramAfterScan = usedRam();
            // There are no classes in any of the JARs.
            assertThat(scanResult.getAllClassesAsMap()).isEmpty();
            // Check if it contains the JAR and all nested entries.
            assertThat(scanResult.getClasspathURLs()).hasSize(1 + 128);
        }
        final long ramAtEnd = usedRam();

        assertThat(ramAfterScan)
                .withFailMessage("Memory usage while using ScanResult should stay within reasonable range: "
                        + "went from %s to %s MB.", ramAtStart / MB, ramAfterScan / MB)
                .isGreaterThanOrEqualTo(ramAtStart).isCloseTo(ramAtStart, offset(MEMORY_TOLERANCE));

        assertThat(ramAtStart)
                .withFailMessage("Memory usage after cleaning up should stay within reasonable range: "
                        + "went from %s to %s MB.", ramAtStart / MB, ramAtEnd / MB)
                .isCloseTo(ramAtEnd, offset(MEMORY_TOLERANCE));
    }

    /**
     * Test jar with stored entries.
     */
    @Test
    public void loadsStoredJarWithManyNestedEntriesAndDoesNotUseMuchMemory() {
        loadsJarWithManyNestedEntriesAndDoesNotUseMuchMemory(
                Issue400.class.getClassLoader().getResource("issue400-nested-stored.jar"));
    }

    /**
     * Test jar with deflated entries.
     */
    @Test
    public void loadsDeflatedJarWithManyNestedEntriesAndDoesNotUseMuchMemory() {
        loadsJarWithManyNestedEntriesAndDoesNotUseMuchMemory(
                Issue400.class.getClassLoader().getResource("issue400-nested-deflated.jar"));
    }

    /**
     * @return used JVM heap size allocated in RAM
     * @see <a href="https://stackoverflow.com/a/42567450/253468">What are Runtime.getRuntime().totalMemory() and
     *      freeMemory()?</a>
     */
    private long usedRam() {
        final Runtime runtime = Runtime.getRuntime();
        runtime.gc();
        System.runFinalization();
        runtime.gc();
        System.runFinalization();
        runtime.gc();
        System.runFinalization();
        return (runtime.totalMemory() - runtime.freeMemory());
    }
}

@@ -618,16 +618,712 @@
  * MB;%0A%0A
+    /**%0A     * @return used JVM heap size allocated in RAM%0A     * @see %3Ca href=%22https://stackoverflow.com/a/42567450/253468%22%3EWhat are Runtime.getRuntime().totalMemory() and%0A     *      freeMemory()?%3C/a%3E%0A     */%0A    private long usedRam() %7B%0A        final Runtime runtime = Runtime.getRuntime();%0A        runtime.gc();%0A        System.runFinalization();%0A        runtime.gc();%0A        System.runFinalization();%0A        runtime.gc();%0A        System.runFinalization();%0A        return (runtime.totalMemory() - runtime.freeMemory());%0A    %7D%0A%0A    /**%0A     * Test whether RAM leaks, or whether nested deflated jars cause large RAM overhead.%0A     * %0A     * @param jars%0A     *            the jar URLs.%0A     */%0A
     @Sup
@@ -1344,16 +1344,16 @@
 %22null%22)%0A
-
     priv
@@ -1609,24 +1609,8 @@
 rs))
-.enableAllInfo()
 %0A   
@@ -1622,16 +1622,59 @@
         
+.ignoreParentClassLoaders().enableAllInfo()
 .scan())
@@ -3265,539 +3265,7 @@
    %7D
-%0A%0A    /**%0A     * @return used JVM heap size allocated in RAM%0A     * @see %3Ca href=%22https://stackoverflow.com/a/42567450/253468%22%3EWhat are Runtime.getRuntime().totalMemory() and%0A     *      freeMemory()?%3C/a%3E%0A     */%0A    private long usedRam() %7B%0A        final Runtime runtime = Runtime.getRuntime();%0A        runtime.gc();%0A        System.runFinalization();%0A        runtime.gc();%0A        System.runFinalization();%0A        runtime.gc();%0A        System.runFinalization();%0A        return (runtime.totalMemory() - runtime.freeMemory());%0A    %7D
 %0A%7D%0A

Reorder methods and ignore parent classloaders