/*
 * Copyright 2007-2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

/* @test
 * @bug 4313887
 * @summary Unit test for java.nio.file.attribute.PosixFileAttributeView
 * @library ../..
 */

import java.nio.file.*;
import static java.nio.file.LinkOption.*;
import java.nio.file.attribute.*;
import java.io.IOException;
import java.util.*;

/**
 * Unit test for PosixFileAttributeView, passing silently if this attribute
 * view is not available.
 */

public class Basic {

    /**
     * Use view to update permission to the given mode and check that the
     * permissions have been updated.
     */
    static void testPermissions(PosixFileAttributeView view, String mode)
        throws IOException
    {
        System.out.format("change mode: %s\n", mode);
        Set<PosixFilePermission> perms = PosixFilePermissions.fromString(mode);

        // change permissions and re-read them.
        view.setPermissions(perms);
        Set<PosixFilePermission> current = view.readAttributes().permissions();
        if (!current.equals(perms)) {
            throw new RuntimeException("Actual permissions: " +
                PosixFilePermissions.toString(current) + ", expected: " +
                PosixFilePermissions.toString(perms));
        }

        // repeat test using setAttribute/getAttribute
        view.setAttribute("permissions", perms);
        current = (Set<PosixFilePermission>)view.getAttribute("permissions");
        if (!current.equals(perms)) {
            throw new RuntimeException("Actual permissions: " +
                PosixFilePermissions.toString(current) + ", expected: " +
                PosixFilePermissions.toString(perms));
        }
    }

    /**
     * Check that the actual permissions of a file match or make it more
     * secure than requested
     */
    static void checkSecure(Set<PosixFilePermission> requested,
                            Set<PosixFilePermission> actual)
    {
        for (PosixFilePermission perm: actual) {
            if (!requested.contains(perm)) {
                throw new RuntimeException("Actual permissions: " +
                    PosixFilePermissions.toString(actual) + ", requested: " +
                    PosixFilePermissions.toString(requested) +
                    " - file is less secure than requested");
            }
        }
    }

    /**
     * Create file with given mode and check that the file is created with a
     * mode that is not less secure
     */
    static void createWithPermissions(Path file,
                                      String mode)
        throws IOException
    {
        Set<PosixFilePermission> requested = PosixFilePermissions.fromString(mode);
        FileAttribute<Set<PosixFilePermission>> attr =
            PosixFilePermissions.asFileAttribute(requested);
        System.out.format("create file with mode: %s\n", mode);

        EnumSet<StandardOpenOption> options = EnumSet.of(StandardOpenOption.CREATE_NEW,
            StandardOpenOption.WRITE);
        file.newOutputStream(options, attr).close();
        try {
            checkSecure(requested,  file
                .getFileAttributeView(PosixFileAttributeView.class)
                .readAttributes()
                .permissions());
        } finally {
            file.delete(false);
        }

        System.out.format("create directory with mode: %s\n", mode);
        file.createDirectory(attr);
        try {
            checkSecure(requested,  file
                .getFileAttributeView(PosixFileAttributeView.class)
                .readAttributes()
                .permissions());
        } finally {
            file.delete(false);
        }
    }

    /**
     * Test the setPermissions/permissions methods.
     */
    static void permissionTests(Path dir)
        throws IOException
    {
        System.out.println("-- Permission Tests  --");

        // create file and test updating and reading its permissions
        Path file = dir.resolve("foo");
        System.out.format("create %s\n", file);
        file.newOutputStream().close();
        try {
            // get initial permissions so that we can restore them later
            PosixFileAttributeView view = file
                .getFileAttributeView(PosixFileAttributeView.class);
            Set<PosixFilePermission> save = view.readAttributes()
                .permissions();

            // test various modes
            try {
                testPermissions(view, "---------");
                testPermissions(view, "r--------");
                testPermissions(view, "-w-------");
                testPermissions(view, "--x------");
                testPermissions(view, "rwx------");
                testPermissions(view, "---r-----");
                testPermissions(view, "----w----");
                testPermissions(view, "-----x---");
                testPermissions(view, "---rwx---");
                testPermissions(view, "------r--");
                testPermissions(view, "-------w-");
                testPermissions(view, "--------x");
                testPermissions(view, "------rwx");
                testPermissions(view, "r--r-----");
                testPermissions(view, "r--r--r--");
                testPermissions(view, "rw-rw----");
                testPermissions(view, "rwxrwx---");
                testPermissions(view, "rw-rw-r--");
                testPermissions(view, "r-xr-x---");
                testPermissions(view, "r-xr-xr-x");
                testPermissions(view, "rwxrwxrwx");
            } finally {
                view.setPermissions(save);
            }
        } finally {
            file.delete(false);
        }

        // create link (to file that doesn't exist) and test reading of
        // permissions
        if (TestUtil.supportsLinks(dir)) {
            Path link = dir.resolve("link");
            System.out.format("create link %s\n", link);
            link.createSymbolicLink(file);
            try {
                PosixFileAttributes attrs = Attributes
                    .readPosixFileAttributes(link, NOFOLLOW_LINKS);
                if (!attrs.isSymbolicLink()) {
                    throw new RuntimeException("not a link");
                }
            } finally {
                link.delete(false);
            }
        }

        System.out.println("OKAY");
    }

    /**
     * Test creating a file and directory with initial permissios
     */
    static void createTests(Path dir)
        throws IOException
    {
        System.out.println("-- Create Tests  --");

        Path file = dir.resolve("foo");

        createWithPermissions(file, "---------");
        createWithPermissions(file, "r--------");
        createWithPermissions(file, "-w-------");
        createWithPermissions(file, "--x------");
        createWithPermissions(file, "rwx------");
        createWithPermissions(file, "---r-----");
        createWithPermissions(file, "----w----");
        createWithPermissions(file, "-----x---");
        createWithPermissions(file, "---rwx---");
        createWithPermissions(file, "------r--");
        createWithPermissions(file, "-------w-");
        createWithPermissions(file, "--------x");
        createWithPermissions(file, "------rwx");
        createWithPermissions(file, "r--r-----");
        createWithPermissions(file, "r--r--r--");
        createWithPermissions(file, "rw-rw----");
        createWithPermissions(file, "rwxrwx---");
        createWithPermissions(file, "rw-rw-r--");
        createWithPermissions(file, "r-xr-x---");
        createWithPermissions(file, "r-xr-xr-x");
        createWithPermissions(file, "rwxrwxrwx");

        System.out.println("OKAY");
    }

    /**
     * Test setOwner/setGroup methods - this test simply exercises the
     * methods to avoid configuration.
     */
    static void ownerTests(Path dir)
        throws IOException
    {
        System.out.println("-- Owner Tests  --");

        Path file = dir.resolve("gus");
        System.out.format("create %s\n", file);

        file.newOutputStream().close();
        try {

            // read attributes of directory to get owner/group
            PosixFileAttributeView view = file
                .getFileAttributeView(PosixFileAttributeView.class);
            PosixFileAttributes attrs = view.readAttributes();

            // set to existing owner/group
            view.setOwner(attrs.owner());
            view.setGroup(attrs.group());

            // repeat test using setAttribute
            Map<String,?> map = view.readAttributes("owner","group");
            view.setAttribute("owner", map.get("owner"));
            view.setAttribute("group", map.get("group"));

        } finally {
            file.delete(false);
        }

        System.out.println("OKAY");
    }

    /**
     * Test the lookupPrincipalByName/lookupPrincipalByGroupName methods
     */
    static void lookupPrincipalTests(Path dir)
        throws IOException
    {
        System.out.println("-- Lookup UserPrincipal Tests --");

        UserPrincipalLookupService lookupService = dir.getFileSystem()
            .getUserPrincipalLookupService();

        // read attributes of directory to get owner/group
        PosixFileAttributes attrs = Attributes.readPosixFileAttributes(dir);

        // lookup owner and check it matches file's owner
        System.out.format("lookup: %s\n", attrs.owner().getName());
        try {
            UserPrincipal owner = lookupService.lookupPrincipalByName(attrs.owner().getName());
            if (owner instanceof GroupPrincipal)
                throw new RuntimeException("owner is a group?");
            if (!owner.equals(attrs.owner()))
                throw new RuntimeException("owner different from file owner");
        } catch (UserPrincipalNotFoundException x) {
            System.out.println("user not found - test skipped");
        }

        // lookup group and check it matches file's group-owner
        System.out.format("lookup group: %s\n", attrs.group().getName());
        try {
            GroupPrincipal group = lookupService.lookupPrincipalByGroupName(attrs.group().getName());
            if (!group.equals(attrs.group()))
                throw new RuntimeException("group different from file group-owner");
        } catch (UserPrincipalNotFoundException x) {
            System.out.println("group not found - test skipped");
        }

        // test that UserPrincipalNotFoundException is thrown
        String invalidPrincipal = "scumbag99";
        try {
            System.out.format("lookup: %s\n", invalidPrincipal);
            lookupService.lookupPrincipalByName(invalidPrincipal);
            throw new RuntimeException("'" + invalidPrincipal + "' is a valid user?");
        } catch (UserPrincipalNotFoundException x) {
        }
        try {
            System.out.format("lookup group: %s\n", invalidPrincipal);
            lookupService.lookupPrincipalByGroupName("idonotexist");
            throw new RuntimeException("'" + invalidPrincipal + "' is a valid group?");
        } catch (UserPrincipalNotFoundException x) {
        }
        System.out.println("OKAY");
    }

    /**
     * Test various exceptions are thrown as expected
     */
    @SuppressWarnings("unchecked")
    static void exceptionsTests(Path dir)
        throws IOException
    {
        System.out.println("-- Exceptions --");

        PosixFileAttributeView view = dir
            .getFileAttributeView(PosixFileAttributeView.class);

        // NullPointerException
        try {
            view.setOwner(null);
            throw new RuntimeException("NullPointerException not thrown");
        } catch (NullPointerException x) {
        }
        try {
            view.setGroup(null);
            throw new RuntimeException("NullPointerException not thrown");
        } catch (NullPointerException x) {
        }

        UserPrincipalLookupService lookupService = dir.getFileSystem()
            .getUserPrincipalLookupService();
        try {
            lookupService.lookupPrincipalByName(null);
            throw new RuntimeException("NullPointerException not thrown");
        } catch (NullPointerException x) {
        }
        try {
            lookupService.lookupPrincipalByGroupName(null);
            throw new RuntimeException("NullPointerException not thrown");
        } catch (NullPointerException x) {
        }
        try {
            view.setPermissions(null);
            throw new RuntimeException("NullPointerException not thrown");
        } catch (NullPointerException x) {
        }
        try {
            Set<PosixFilePermission> perms = new HashSet<PosixFilePermission>();
            perms.add(null);
            view.setPermissions(perms);
            throw new RuntimeException("NullPointerException not thrown");
        }  catch (NullPointerException x) {
        }

        // ClassCastException
        try {
            Set perms = new HashSet();  // raw type
            perms.add(new Object());
            view.setPermissions(perms);
            throw new RuntimeException("ClassCastException not thrown");
        }  catch (ClassCastException x) {
        }

        System.out.println("OKAY");
    }

    public static void main(String[] args) throws IOException {
        Path dir = TestUtil.createTemporaryDirectory();
        try {
            if (!dir.getFileStore().supportsFileAttributeView("posix")) {
                System.out.println("PosixFileAttributeView not supported");
                return;
            }

            permissionTests(dir);
            createTests(dir);
            ownerTests(dir);
            lookupPrincipalTests(dir);
            exceptionsTests(dir);

        } finally {
            TestUtil.removeAll(dir);
        }
    }
}
