org.alfresco.repo.node.getchildren.GetChildrenCannedQueryTest.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.repo.node.getchildren.GetChildrenCannedQueryTest.java

Source

/*
 * #%L
 * Alfresco Repository
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Alfresco 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */
package org.alfresco.repo.node.getchildren;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.net.URL;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import junit.framework.TestCase;

import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.model.RenditionModel;
import org.alfresco.query.CannedQueryFactory;
import org.alfresco.query.CannedQueryResults;
import org.alfresco.query.PagingRequest;
import org.alfresco.query.PagingResults;
import org.alfresco.repo.dictionary.DictionaryBootstrap;
import org.alfresco.repo.dictionary.DictionaryDAO;
import org.alfresco.repo.domain.contentdata.ContentDataDAO;
import org.alfresco.repo.domain.locale.LocaleDAO;
import org.alfresco.repo.domain.node.NodeDAO;
import org.alfresco.repo.domain.qname.QNameDAO;
import org.alfresco.repo.domain.query.CannedQueryDAO;
import org.alfresco.repo.model.Repository;
import org.alfresco.repo.model.filefolder.GetChildrenCannedQueryFactory;
import org.alfresco.repo.model.filefolder.HiddenAspect;
import org.alfresco.repo.node.getchildren.FilterPropString.FilterTypeString;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityBean;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.rating.RatingScheme;
import org.alfresco.service.cmr.rating.RatingService;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.test_category.OwnJVMTestsCategory;
import org.alfresco.util.AlfrescoCollator;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.Pair;
import org.alfresco.util.PropertyMap;
import org.alfresco.util.registry.NamedObjectRegistry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.experimental.categories.Category;
import org.springframework.context.ApplicationContext;
import org.springframework.extensions.surf.util.I18NUtil;

/**
 * GetChildren canned query - simple unit tests
 * 
 * @author janv
 * @since 4.0
 */
@Category(OwnJVMTestsCategory.class)
public class GetChildrenCannedQueryTest extends TestCase {
    private Log logger = LogFactory.getLog(getClass());

    private static final ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();

    private Repository repositoryHelper;
    private NodeService nodeService;
    private TransactionService transactionService;
    private DictionaryService dictionaryService;
    private ContentService contentService;
    private MimetypeService mimetypeService;

    private PersonService personService;
    private MutableAuthenticationService authenticationService;
    private PermissionService permissionService;
    private RatingService ratingService;
    private TenantService tenantService;
    private DictionaryDAO dictionaryDAO;

    private RatingScheme fiveStarRatingScheme;
    private RatingScheme likesRatingScheme;
    private Random random = new Random();

    private static boolean setupTestData = false;

    private static final String TEST_RUN = System.currentTimeMillis() + "";
    private static final String TEST_FILE_PREFIX = "GC-CQ-File-" + TEST_RUN + "-";
    private static final String TEST_USER_PREFIX = "GC-CQ-User-" + TEST_RUN + "-";

    private static final String TEST_USER = TEST_USER_PREFIX + "user";

    private static QName TEST_CONTENT_SUBTYPE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,
            "savedquery");
    private static QName TEST_FOLDER_SUBTYPE = ContentModel.TYPE_SYSTEM_FOLDER;

    private static final String TEST_RUN_ID = "" + System.currentTimeMillis();

    private static final String FOLDER_1 = "aaa-folder";
    private static final String FOLDER_2 = "bbb-folder";
    private static final String FOLDER_3 = "emptySystemFolder"; // note: using FileFolderService directly will auto-hide this folder
    private static final String FOLDER_4 = "yyy-folder";
    private static final String FOLDER_5 = "zzz-folder";

    private Set<NodeRef> permHits = new HashSet<NodeRef>(100);
    private Set<NodeRef> permMisses = new HashSet<NodeRef>(100);

    @SuppressWarnings({ "rawtypes" })
    private NamedObjectRegistry<CannedQueryFactory> cannedQueryRegistry;
    private static final String CQ_FACTORY_NAME = "fileFolderGetChildrenCannedQueryFactory";

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    public void setUp() throws Exception {
        repositoryHelper = (Repository) ctx.getBean("repositoryHelper");

        nodeService = (NodeService) ctx.getBean("NodeService");
        transactionService = (TransactionService) ctx.getBean("TransactionService");
        contentService = (ContentService) ctx.getBean("ContentService");
        mimetypeService = (MimetypeService) ctx.getBean("MimetypeService");
        dictionaryService = (DictionaryService) ctx.getBean("DictionaryService");

        personService = (PersonService) ctx.getBean("PersonService");
        authenticationService = (MutableAuthenticationService) ctx.getBean("AuthenticationService");
        permissionService = (PermissionService) ctx.getBean("PermissionService");
        ratingService = (RatingService) ctx.getBean("RatingService");

        dictionaryDAO = (DictionaryDAO) ctx.getBean("dictionaryDAO");
        tenantService = (TenantService) ctx.getBean("tenantService");

        cannedQueryRegistry = new NamedObjectRegistry<CannedQueryFactory>();
        cannedQueryRegistry.setStorageType(CannedQueryFactory.class);

        GetChildrenCannedQueryFactory getChildrenCannedQueryFactory = new GetChildrenCannedQueryFactory();

        getChildrenCannedQueryFactory.setBeanName("fileFolderGetChildrenCannedQueryFactory");
        getChildrenCannedQueryFactory.setRegistry(cannedQueryRegistry);

        getChildrenCannedQueryFactory.setCannedQueryDAO((CannedQueryDAO) ctx.getBean("cannedQueryDAO"));
        getChildrenCannedQueryFactory.setContentDataDAO((ContentDataDAO) ctx.getBean("contentDataDAO"));
        getChildrenCannedQueryFactory.setDictionaryService((DictionaryService) ctx.getBean("dictionaryService"));
        getChildrenCannedQueryFactory.setTenantService((TenantService) ctx.getBean("tenantService"));
        getChildrenCannedQueryFactory.setLocaleDAO((LocaleDAO) ctx.getBean("localeDAO"));
        getChildrenCannedQueryFactory.setNodeDAO((NodeDAO) ctx.getBean("nodeDAO"));
        getChildrenCannedQueryFactory.setNodeService(nodeService);
        getChildrenCannedQueryFactory.setQnameDAO((QNameDAO) ctx.getBean("qnameDAO"));
        getChildrenCannedQueryFactory.setHiddenAspect((HiddenAspect) ctx.getBean("hiddenAspect"));

        getChildrenCannedQueryFactory
                .setMethodSecurity((MethodSecurityBean<NodeRef>) ctx.getBean("FileFolderService_security_list"));

        getChildrenCannedQueryFactory.afterPropertiesSet();

        fiveStarRatingScheme = ratingService.getRatingScheme("fiveStarRatingScheme");
        assertNotNull(fiveStarRatingScheme);
        likesRatingScheme = ratingService.getRatingScheme("likesRatingScheme");
        assertNotNull(likesRatingScheme);

        if (!setupTestData) {
            AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

            // Load test model
            DictionaryBootstrap bootstrap = new DictionaryBootstrap();
            List<String> bootstrapModels = new ArrayList<String>();
            bootstrapModels.add("org/alfresco/repo/node/getchildren/testModel.xml");
            List<String> labels = new ArrayList<String>();
            bootstrap.setModels(bootstrapModels);
            bootstrap.setLabels(labels);
            bootstrap.setDictionaryDAO(dictionaryDAO);
            bootstrap.setTenantService(tenantService);
            bootstrap.bootstrap();

            createUser(TEST_USER_PREFIX, TEST_USER, TEST_USER);

            createUser(TEST_USER_PREFIX + "aaaa", TEST_USER_PREFIX + "bbbb", TEST_USER_PREFIX + "cccc");
            createUser(TEST_USER_PREFIX + "cccc", TEST_USER_PREFIX + "dddd", TEST_USER_PREFIX + "eeee");
            createUser(TEST_USER_PREFIX + "dddd", TEST_USER_PREFIX + "ffff", TEST_USER_PREFIX + "gggg");
            createUser(TEST_USER_PREFIX + "hhhh", TEST_USER_PREFIX + "cccc", TEST_USER_PREFIX + "jjjj");

            NodeRef testParentFolder = getOrCreateParentTestFolder("GetChildrenCannedQueryTest-" + TEST_RUN_ID);

            // create folder subtype (note: system folder here)
            createFolder(testParentFolder, FOLDER_3, TEST_FOLDER_SUBTYPE);

            // create content subtype (note: no pun intended ... "cm:savedquery" already exists in content model ... but is NOT related to canned queries !)
            createContent(testParentFolder, "textContent", TEST_CONTENT_SUBTYPE);

            createFolder(testParentFolder, FOLDER_5, ContentModel.TYPE_FOLDER);
            createFolder(testParentFolder, FOLDER_4, ContentModel.TYPE_FOLDER);

            boolean canRead = true;

            loadContent(testParentFolder, "quick.jpg", "", "", canRead, permHits);
            loadContent(testParentFolder, "quick.txt", "ZZ title " + TEST_RUN, "ZZ description 1", canRead,
                    permHits);
            loadContent(testParentFolder, "quick.bmp", null, null, canRead, permHits);
            loadContent(testParentFolder, "quick.doc", "BB title " + TEST_RUN, "BB description", canRead, permHits);
            loadContent(testParentFolder, "quick.pdf", "ZZ title " + TEST_RUN, "ZZ description 2", canRead,
                    permHits);

            canRead = false;

            loadContent(testParentFolder, "quick.ppt", "CC title " + TEST_RUN, "CC description", canRead,
                    permMisses);
            loadContent(testParentFolder, "quick.xls", "AA title " + TEST_RUN, "AA description", canRead,
                    permMisses);
            loadContent(testParentFolder, "quick.gif", "YY title " + TEST_RUN, "BB description", canRead,
                    permMisses);
            loadContent(testParentFolder, "quick.xml", "ZZ title" + TEST_RUN, "BB description", canRead,
                    permMisses);

            createFolder(testParentFolder, FOLDER_2, ContentModel.TYPE_FOLDER);
            createFolder(testParentFolder, FOLDER_1, ContentModel.TYPE_FOLDER);

            setupTestData = true;

            // double-check permissions - see testPermissions

            AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER);

            for (NodeRef nodeRef : permHits) {
                assertTrue(
                        permissionService.hasPermission(nodeRef, PermissionService.READ) == AccessStatus.ALLOWED);
            }

            for (NodeRef nodeRef : permMisses) {
                // user CANNOT read
                assertFalse(
                        permissionService.hasPermission(nodeRef, PermissionService.READ) == AccessStatus.ALLOWED);
            }

            // belts-and-braces
            AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

            for (NodeRef nodeRef : permHits) {
                assertTrue(
                        permissionService.hasPermission(nodeRef, PermissionService.READ) == AccessStatus.ALLOWED);
            }

            for (NodeRef nodeRef : permMisses) {
                // admin CAN read
                assertTrue(
                        permissionService.hasPermission(nodeRef, PermissionService.READ) == AccessStatus.ALLOWED);
            }
        }

        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        NodeRef testFolder = createFolder(repositoryHelper.getCompanyHome(),
                "GetChildrenCannedQueryTest-testFolder-" + TEST_RUN_ID,
                QName.createQName("http://www.alfresco.org/test/getchildrentest/1.0", "folder"));
        createContent(testFolder, "textContent1", ContentModel.TYPE_CONTENT);
        createContent(testFolder,
                QName.createQName("http://www.alfresco.org/test/getchildrentest/1.0", "contains1"), "textContent2",
                ContentModel.TYPE_CONTENT);

        AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER);
    }

    private NodeRef getOrCreateParentTestFolder(String name) throws Exception {
        NodeRef testFolder = nodeService.getChildByName(repositoryHelper.getCompanyHome(),
                ContentModel.ASSOC_CONTAINS, name);
        if (testFolder == null) {
            testFolder = createFolder(repositoryHelper.getCompanyHome(), name, ContentModel.TYPE_FOLDER);
        }
        return testFolder;
    }

    public void testSetup() throws Exception {
        NodeRef parentNodeRef = getOrCreateParentTestFolder("GetChildrenCannedQueryTest-" + TEST_RUN_ID);

        PagingResults<NodeRef> results = list(parentNodeRef, -1, -1, 0);
        assertTrue(results.getPage().size() > 3);
    }

    public void testMaxItems() throws Exception {
        NodeRef parentNodeRef = getOrCreateParentTestFolder("GetChildrenCannedQueryTest-" + TEST_RUN_ID);

        PagingResults<NodeRef> results = list(parentNodeRef, -1, -1, 0);
        assertFalse(results.hasMoreItems());

        int totalCnt = results.getPage().size();
        assertTrue(totalCnt > 3);

        for (int i = 1; i <= totalCnt; i++) {
            results = list(parentNodeRef, 0, i, 0);
            assertEquals(results.getPage().size(), i);

            boolean hasMore = results.hasMoreItems();
            assertTrue(hasMore == (i != totalCnt));

            if (logger.isInfoEnabled()) {
                logger.info("testSimpleMaxItems: [itemCnt=" + i + ",hasMore=" + hasMore + "]");
            }
        }
    }

    public void testPaging() throws Exception {
        NodeRef parentNodeRef = getOrCreateParentTestFolder("GetChildrenCannedQueryTest-" + TEST_RUN_ID);

        PagingResults<NodeRef> results = list(parentNodeRef, -1, -1, 0);
        assertFalse(results.hasMoreItems());

        int totalCnt = results.getPage().size();
        int pageSize = 3;
        assertTrue(totalCnt > pageSize);

        int pageCnt = totalCnt / pageSize;
        if ((totalCnt % pageSize) != 0) {
            // round-up
            pageCnt++;
        }
        assertTrue(pageCnt > 1);

        if (logger.isInfoEnabled()) {
            logger.info("testSimplePaging: [totalCount=" + totalCnt + ",pageSize=" + pageSize + ",pageCount="
                    + pageCnt + "]");
        }

        for (int i = 1; i <= pageCnt; i++) {
            int skipCount = ((i - 1) * pageSize);
            int maxItems = pageSize;

            results = list(parentNodeRef, skipCount, maxItems, 0);

            boolean hasMore = results.hasMoreItems();
            int itemsCnt = results.getPage().size();

            if (logger.isInfoEnabled()) {
                logger.info("testSimplePaging:     [pageNum=" + i + ",itemCnt=" + itemsCnt + ",hasMore=" + hasMore
                        + "]");
            }

            if (i != pageCnt) {
                // not last page
                assertEquals(itemsCnt, maxItems);
                assertTrue(hasMore);
            } else {
                // last page
                assertTrue(itemsCnt <= maxItems);
                assertFalse(hasMore);
            }
        }
    }

    public void testTypeFiltering() throws Exception {
        NodeRef parentNodeRef = getOrCreateParentTestFolder("GetChildrenCannedQueryTest-" + TEST_RUN_ID);

        // note: parent should contain test example(s) of each type

        Set<QName> childTypeQNames = new HashSet<QName>(3);
        Set<QName> antiChildTypeQNames = new HashSet<QName>(3);

        // note: subtype != supertype

        // folders

        childTypeQNames.clear();
        childTypeQNames.add(ContentModel.TYPE_FOLDER);

        antiChildTypeQNames.clear();
        antiChildTypeQNames.add(TEST_FOLDER_SUBTYPE);

        filterByTypeAndCheck(parentNodeRef, childTypeQNames, antiChildTypeQNames);

        // files (content)

        childTypeQNames.clear();
        childTypeQNames.add(ContentModel.TYPE_CONTENT);

        antiChildTypeQNames.clear();
        antiChildTypeQNames.add(TEST_CONTENT_SUBTYPE);

        filterByTypeAndCheck(parentNodeRef, childTypeQNames, antiChildTypeQNames);

        // folders and files (base types)

        childTypeQNames.clear();
        childTypeQNames.add(ContentModel.TYPE_CONTENT);
        childTypeQNames.add(ContentModel.TYPE_FOLDER);

        antiChildTypeQNames.clear();
        antiChildTypeQNames.add(TEST_CONTENT_SUBTYPE);
        antiChildTypeQNames.add(TEST_FOLDER_SUBTYPE);

        filterByTypeAndCheck(parentNodeRef, childTypeQNames, antiChildTypeQNames);

        // folders and files (specific subtypes)

        childTypeQNames.clear();
        childTypeQNames.add(TEST_CONTENT_SUBTYPE);
        childTypeQNames.add(TEST_FOLDER_SUBTYPE);

        antiChildTypeQNames.clear();
        antiChildTypeQNames.add(ContentModel.TYPE_CONTENT);
        antiChildTypeQNames.add(ContentModel.TYPE_FOLDER);

        filterByTypeAndCheck(parentNodeRef, childTypeQNames, antiChildTypeQNames);

        // Specific super-type (that likely does not exist in DB, at least yet - see ACE-5114 - alternatively could create custom type to ensure this)
        // note: results should return 0

        childTypeQNames.clear();
        childTypeQNames.add(ContentModel.TYPE_LINK);

        PagingResults<NodeRef> results = list(parentNodeRef, -1, -1, 0, childTypeQNames, null, null);
        assertEquals(0, results.getPage().size());

        childTypeQNames.clear();
        childTypeQNames.add(ContentModel.TYPE_CMOBJECT);

        results = list(parentNodeRef, -1, -1, 0, childTypeQNames, null, null);
        assertEquals(0, results.getPage().size());
    }

    public void testPrimaryVsSecondary() throws Exception {
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        NodeRef userHomeRef = repositoryHelper.getCompanyHome();

        NodeRef parentNodeRef1 = createFolder(userHomeRef,
                "GetChildrenCannedQueryTest-PrimaryVsSecondary-" + TEST_RUN_ID, ContentModel.TYPE_FOLDER);

        PagingResults<NodeRef> results = list(parentNodeRef1, -1, -1, 0, null, null, null);
        assertEquals(0, results.getPage().size());

        List<FilterProp> filterPropsPrimary = new ArrayList<>(1);
        filterPropsPrimary.add(new FilterPropBoolean(GetChildrenCannedQuery.FILTER_QNAME_NODE_IS_PRIMARY, true));

        List<FilterProp> filterPropsSecondary = new ArrayList<>(1);
        filterPropsSecondary.add(new FilterPropBoolean(GetChildrenCannedQuery.FILTER_QNAME_NODE_IS_PRIMARY, false));

        results = list(parentNodeRef1, -1, -1, 0, null, filterPropsPrimary, null);
        assertEquals(0, results.getPage().size());

        results = list(parentNodeRef1, -1, -1, 0, null, filterPropsSecondary, null);
        assertEquals(0, results.getPage().size());

        NodeRef folder1Ref = createFolder(parentNodeRef1, FOLDER_1, ContentModel.TYPE_FOLDER);
        NodeRef folder2Ref = createFolder(parentNodeRef1, FOLDER_2, ContentModel.TYPE_FOLDER);

        results = list(parentNodeRef1, -1, -1, 0, null, null, null);
        assertEquals(2, results.getPage().size());
        assertTrue(results.getPage().contains(folder1Ref));
        assertTrue(results.getPage().contains(folder2Ref));

        results = list(parentNodeRef1, -1, -1, 0, null, filterPropsPrimary, null);
        assertEquals(2, results.getPage().size());
        assertTrue(results.getPage().contains(folder1Ref));
        assertTrue(results.getPage().contains(folder2Ref));

        results = list(parentNodeRef1, -1, -1, 0, null, filterPropsSecondary, null);
        assertEquals(0, results.getPage().size());

        NodeRef parentNodeRef2 = getOrCreateParentTestFolder("GetChildrenCannedQueryTest-2-" + TEST_RUN_ID);

        NodeRef folder3Ref = createFolder(parentNodeRef2, FOLDER_3, ContentModel.TYPE_FOLDER);

        nodeService.addChild(parentNodeRef1, folder3Ref, ContentModel.ASSOC_CONTAINS, QName.createQName("cm:2nd"));

        results = list(parentNodeRef1, -1, -1, 0, null, null, null);
        assertEquals(3, results.getPage().size());
        assertTrue(results.getPage().contains(folder1Ref));
        assertTrue(results.getPage().contains(folder2Ref));
        assertTrue(results.getPage().contains(folder3Ref));

        results = list(parentNodeRef1, -1, -1, 0, null, filterPropsPrimary, null);
        assertEquals(2, results.getPage().size());
        assertTrue(results.getPage().contains(folder1Ref));
        assertTrue(results.getPage().contains(folder2Ref));

        results = list(parentNodeRef1, -1, -1, 0, null, filterPropsSecondary, null);
        assertEquals(1, results.getPage().size());
        assertTrue(results.getPage().contains(folder3Ref));
    }

    public void testPropertyStringFiltering() throws Exception {
        NodeRef parentNodeRef = getOrCreateParentTestFolder("GetChildrenCannedQueryTest-" + TEST_RUN_ID);

        filterByPropAndCheck(parentNodeRef, ContentModel.PROP_NAME, "GC-CQ-File-" + TEST_RUN + "-",
                FilterTypeString.STARTSWITH, 5);
        filterByPropAndCheck(parentNodeRef, ContentModel.PROP_NAME, "gc-CQ-File-" + TEST_RUN + "-",
                FilterTypeString.STARTSWITH, 0);
        filterByPropAndCheck(parentNodeRef, ContentModel.PROP_NAME, "gC-CQ-File-" + TEST_RUN + "-",
                FilterTypeString.STARTSWITH_IGNORECASE, 5);
        filterByPropAndCheck(parentNodeRef, ContentModel.PROP_NAME, "CQ-CQ-File-" + TEST_RUN + "-",
                FilterTypeString.STARTSWITH_IGNORECASE, 0);

        filterByPropAndCheck(parentNodeRef, ContentModel.PROP_TITLE, "ZZ title " + TEST_RUN,
                FilterTypeString.EQUALS, 2);
        filterByPropAndCheck(parentNodeRef, ContentModel.PROP_TITLE, "zz title " + TEST_RUN,
                FilterTypeString.EQUALS, 0);
        filterByPropAndCheck(parentNodeRef, ContentModel.PROP_TITLE, "zZ tItLe " + TEST_RUN,
                FilterTypeString.EQUALS_IGNORECASE, 2);
        filterByPropAndCheck(parentNodeRef, ContentModel.PROP_TITLE, "title " + TEST_RUN, FilterTypeString.EQUALS,
                0);

        // filter with two props
        List<FilterProp> filterProps = new ArrayList<FilterProp>(4);
        filterProps.add(new FilterPropString(ContentModel.PROP_USERNAME, TEST_USER_PREFIX + "dddd",
                FilterTypeString.STARTSWITH_IGNORECASE));
        filterProps.add(new FilterPropString(ContentModel.PROP_FIRSTNAME, TEST_USER_PREFIX + "dddd",
                FilterTypeString.STARTSWITH_IGNORECASE));

        NodeRef peopleContainerRef = personService.getPeopleContainer();
        PagingResults<NodeRef> results = list(peopleContainerRef, -1, -1, 0, null, filterProps, null);
        assertEquals(2, results.getPage().size());

        // filter with three props
        filterProps.clear();
        filterProps.add(new FilterPropString(ContentModel.PROP_USERNAME, TEST_USER_PREFIX + "cccc",
                FilterTypeString.STARTSWITH_IGNORECASE));
        filterProps.add(new FilterPropString(ContentModel.PROP_FIRSTNAME, TEST_USER_PREFIX + "cccc",
                FilterTypeString.STARTSWITH_IGNORECASE));
        filterProps.add(new FilterPropString(ContentModel.PROP_LASTNAME, TEST_USER_PREFIX + "cccc",
                FilterTypeString.STARTSWITH_IGNORECASE));

        results = list(peopleContainerRef, -1, -1, 0, null, filterProps, null);
        assertEquals(3, results.getPage().size());

        filterProps.clear();
        filterProps.add(new FilterPropString(ContentModel.PROP_USERNAME, TEST_USER_PREFIX + "aaaa",
                FilterTypeString.STARTSWITH_IGNORECASE));
        filterProps.add(new FilterPropString(ContentModel.PROP_FIRSTNAME, TEST_USER_PREFIX + "aaaa",
                FilterTypeString.STARTSWITH_IGNORECASE));
        filterProps.add(new FilterPropString(ContentModel.PROP_LASTNAME, TEST_USER_PREFIX + "aaaa",
                FilterTypeString.STARTSWITH_IGNORECASE));

        results = list(peopleContainerRef, -1, -1, 0, null, filterProps, null);
        assertEquals(1, results.getPage().size());

        filterProps.clear();
        filterProps.add(new FilterPropString(ContentModel.PROP_USERNAME, TEST_USER_PREFIX + "ffff",
                FilterTypeString.STARTSWITH_IGNORECASE));
        filterProps.add(new FilterPropString(ContentModel.PROP_FIRSTNAME, TEST_USER_PREFIX + "ffff",
                FilterTypeString.STARTSWITH_IGNORECASE));
        filterProps.add(new FilterPropString(ContentModel.PROP_LASTNAME, TEST_USER_PREFIX + "ffff",
                FilterTypeString.STARTSWITH_IGNORECASE));

        results = list(peopleContainerRef, -1, -1, 0, null, filterProps, null);
        assertEquals(1, results.getPage().size());

        filterProps.clear();
        filterProps.add(new FilterPropString(ContentModel.PROP_USERNAME, TEST_USER_PREFIX + "jjjj",
                FilterTypeString.STARTSWITH_IGNORECASE));
        filterProps.add(new FilterPropString(ContentModel.PROP_FIRSTNAME, TEST_USER_PREFIX + "jjjj",
                FilterTypeString.STARTSWITH_IGNORECASE));
        filterProps.add(new FilterPropString(ContentModel.PROP_LASTNAME, TEST_USER_PREFIX + "jjjj",
                FilterTypeString.STARTSWITH_IGNORECASE));

        results = list(peopleContainerRef, -1, -1, 0, null, filterProps, null);
        assertEquals(1, results.getPage().size());

        // try to filter with more than three props
        filterProps.clear();
        filterProps.add(new FilterPropString(ContentModel.PROP_NAME, "a", FilterTypeString.STARTSWITH_IGNORECASE));
        filterProps.add(new FilterPropString(ContentModel.PROP_TITLE, "a", FilterTypeString.STARTSWITH_IGNORECASE));
        filterProps.add(
                new FilterPropString(ContentModel.PROP_DESCRIPTION, "a", FilterTypeString.STARTSWITH_IGNORECASE));
        filterProps
                .add(new FilterPropString(ContentModel.PROP_CREATOR, "a", FilterTypeString.STARTSWITH_IGNORECASE));
        try {
            // -ve test
            results = list(parentNodeRef, -1, -1, 0, null, filterProps, null);
            fail("Unexpected - cannot filter with more than three props");
        } catch (AlfrescoRuntimeException are) {
            // expected
        }
    }

    private float getFiveStarRating() {
        float rating = random.nextFloat()
                * (fiveStarRatingScheme.getMaxRating() - fiveStarRatingScheme.getMinRating())
                + fiveStarRatingScheme.getMinRating();
        assertTrue("Five star rating is out of range: " + rating,
                (rating >= fiveStarRatingScheme.getMinRating() && rating <= fiveStarRatingScheme.getMaxRating()));
        return rating;
    }

    public void testPropertySorting() throws Exception {
        NodeRef parentNodeRef = getOrCreateParentTestFolder("GetChildrenCannedQueryTest-" + TEST_RUN_ID);

        NodeRef nodeRef1 = null;
        NodeRef nodeRef2 = null;
        NodeRef nodeRef3 = null;
        NodeRef nodeRef4 = null;
        NodeRef nodeRef5 = null;
        NodeRef nodeRef6 = null;
        NodeRef nodeRef7 = null;
        NodeRef nodeRef8 = null;
        NodeRef nodeRef9 = null;
        NodeRef nodeRef10 = null;

        // Create some nodes with integer properties on which to sort, in this case cm:fiveStarRatingScheme and cm:likesRatingScheme
        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
        try {
            nodeRef1 = createContent(parentNodeRef, "rating1", ContentModel.TYPE_CONTENT);
            nodeRef2 = createContent(parentNodeRef, "rating2", ContentModel.TYPE_CONTENT);
            nodeRef3 = createContent(parentNodeRef, "rating3", ContentModel.TYPE_CONTENT);
            nodeRef4 = createContent(parentNodeRef, "rating4", ContentModel.TYPE_CONTENT);
            nodeRef5 = createContent(parentNodeRef, "rating5", ContentModel.TYPE_CONTENT);

            nodeRef6 = createContent(parentNodeRef, "rating6", ContentModel.TYPE_CONTENT);
            nodeRef7 = createContent(parentNodeRef, "rating7", ContentModel.TYPE_CONTENT);
            nodeRef8 = createContent(parentNodeRef, "rating8", ContentModel.TYPE_CONTENT);
            nodeRef9 = createContent(parentNodeRef, "rating9", ContentModel.TYPE_CONTENT);
            nodeRef10 = createContent(parentNodeRef, "rating10", ContentModel.TYPE_CONTENT);
        } finally {
            AuthenticationUtil.popAuthentication();
        }

        // apply some ratings to these nodes
        ratingService.applyRating(nodeRef1, getFiveStarRating(), "fiveStarRatingScheme");
        ratingService.applyRating(nodeRef2, getFiveStarRating(), "fiveStarRatingScheme");
        ratingService.applyRating(nodeRef3, getFiveStarRating(), "fiveStarRatingScheme");
        ratingService.applyRating(nodeRef4, getFiveStarRating(), "fiveStarRatingScheme");
        ratingService.applyRating(nodeRef5, getFiveStarRating(), "fiveStarRatingScheme");

        ratingService.applyRating(nodeRef6, 1.0f, "likesRatingScheme");
        ratingService.applyRating(nodeRef7, 1.0f, "likesRatingScheme");
        ratingService.applyRating(nodeRef8, 1.0f, "likesRatingScheme");
        ratingService.applyRating(nodeRef9, 1.0f, "likesRatingScheme");
        ratingService.applyRating(nodeRef10, 1.0f, "likesRatingScheme");

        // do children canned query
        PagingResults<NodeRef> results = list(parentNodeRef, -1, -1, 0);

        List<QName> sortQNames = new ArrayList<QName>(3);

        // note: initial test list derived from default Share DocLib ("share-documentlibrary-config.xml")

        sortQNames.add(ContentModel.PROP_NAME);
        sortQNames.add(ContentModel.PROP_TITLE);
        sortQNames.add(ContentModel.PROP_DESCRIPTION);
        sortQNames.add(ContentModel.PROP_CREATED);
        sortQNames.add(ContentModel.PROP_CREATOR);
        sortQNames.add(ContentModel.PROP_MODIFIED);
        sortQNames.add(ContentModel.PROP_MODIFIER);

        // special (pseudo) content/node props
        sortQNames.add(GetChildrenCannedQuery.SORT_QNAME_CONTENT_SIZE);
        sortQNames.add(GetChildrenCannedQuery.SORT_QNAME_CONTENT_MIMETYPE);
        sortQNames.add(GetChildrenCannedQuery.SORT_QNAME_NODE_TYPE);
        sortQNames.add(GetChildrenCannedQuery.SORT_QNAME_NODE_IS_FOLDER);

        // Add in the ratings properties on which to sort
        sortQNames.add(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "fiveStarRatingSchemeCount"));
        sortQNames.add(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "likesRatingSchemeCount"));

        // sort with one prop
        for (QName sortQName : sortQNames) {
            sortAndCheck(parentNodeRef, sortQName, false); // descending
            sortAndCheck(parentNodeRef, sortQName, true); // ascending
        }

        // sort with two props - title first, then description
        List<Pair<QName, Boolean>> sortPairs = new ArrayList<Pair<QName, Boolean>>(3);
        sortPairs.add(new Pair<QName, Boolean>(ContentModel.PROP_TITLE, false));
        sortPairs.add(new Pair<QName, Boolean>(ContentModel.PROP_DESCRIPTION, false));

        results = list(parentNodeRef, -1, -1, 0, null, null, sortPairs);
        assertEquals(TEST_FILE_PREFIX + "quick.pdf",
                nodeService.getProperty(results.getPage().get(0), ContentModel.PROP_NAME)); // ZZ title + YY description
        assertEquals(TEST_FILE_PREFIX + "quick.txt",
                nodeService.getProperty(results.getPage().get(1), ContentModel.PROP_NAME)); // ZZ title + XX description

        // sort with two props - folders first (ALF-13968) then by name
        sortPairs = new ArrayList<Pair<QName, Boolean>>(3);
        sortPairs.add(new Pair<QName, Boolean>(GetChildrenCannedQuery.SORT_QNAME_NODE_IS_FOLDER, false));
        sortPairs.add(new Pair<QName, Boolean>(ContentModel.PROP_NAME, true));

        results = list(parentNodeRef, -1, -1, 0, null, null, sortPairs);

        assertEquals(FOLDER_1, nodeService.getProperty(results.getPage().get(0), ContentModel.PROP_NAME));
        assertEquals(FOLDER_2, nodeService.getProperty(results.getPage().get(1), ContentModel.PROP_NAME));
        assertEquals(FOLDER_3, nodeService.getProperty(results.getPage().get(2), ContentModel.PROP_NAME));
        assertEquals(FOLDER_4, nodeService.getProperty(results.getPage().get(3), ContentModel.PROP_NAME));
        assertEquals(FOLDER_5, nodeService.getProperty(results.getPage().get(4), ContentModel.PROP_NAME));

        assertEquals(TEST_FILE_PREFIX + "quick.bmp",
                nodeService.getProperty(results.getPage().get(5), ContentModel.PROP_NAME));
        assertEquals(TEST_FILE_PREFIX + "quick.doc",
                nodeService.getProperty(results.getPage().get(6), ContentModel.PROP_NAME));

        sortPairs = new ArrayList<Pair<QName, Boolean>>(3);
        sortPairs.add(new Pair<QName, Boolean>(GetChildrenCannedQuery.SORT_QNAME_NODE_IS_FOLDER, true));
        sortPairs.add(new Pair<QName, Boolean>(ContentModel.PROP_NAME, false));

        results = list(parentNodeRef, -1, -1, 0, null, null, sortPairs);

        // note: this test assumes that children fit on one page
        int len = results.getPage().size();

        assertEquals("textContent", nodeService.getProperty(results.getPage().get(0), ContentModel.PROP_NAME));
        assertEquals("rating9", nodeService.getProperty(results.getPage().get(1), ContentModel.PROP_NAME));

        assertEquals(FOLDER_5, nodeService.getProperty(results.getPage().get(len - 5), ContentModel.PROP_NAME));
        assertEquals(FOLDER_4, nodeService.getProperty(results.getPage().get(len - 4), ContentModel.PROP_NAME));
        assertEquals(FOLDER_3, nodeService.getProperty(results.getPage().get(len - 3), ContentModel.PROP_NAME));
        assertEquals(FOLDER_2, nodeService.getProperty(results.getPage().get(len - 2), ContentModel.PROP_NAME));
        assertEquals(FOLDER_1, nodeService.getProperty(results.getPage().get(len - 1), ContentModel.PROP_NAME));

        // TODO - sort with three props

        // try to sort with more than three props
        try {
            // -ve test
            sortPairs = new ArrayList<Pair<QName, Boolean>>(3);
            sortPairs.add(new Pair<QName, Boolean>(ContentModel.PROP_NAME, true));
            sortPairs.add(new Pair<QName, Boolean>(ContentModel.PROP_TITLE, true));
            sortPairs.add(new Pair<QName, Boolean>(ContentModel.PROP_DESCRIPTION, true));
            sortPairs.add(new Pair<QName, Boolean>(ContentModel.PROP_MODIFIED, true));

            results = list(parentNodeRef, -1, -1, 0, null, null, sortPairs);
            fail("Unexpected - cannot sort with more than three props");
        } catch (AlfrescoRuntimeException are) {
            // expected
        }
    }

    public void testPermissions() throws Exception {
        AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER);

        NodeRef parentNodeRef = getOrCreateParentTestFolder("GetChildrenCannedQueryTest-" + TEST_RUN_ID);

        PagingResults<NodeRef> results = list(parentNodeRef, -1, -1, 0);
        assertFalse(results.hasMoreItems());

        List<NodeRef> nodeRefs = results.getPage();

        for (NodeRef nodeRef : permHits) {
            assertTrue(nodeRefs.contains(nodeRef));
        }

        for (NodeRef nodeRef : permMisses) {
            assertFalse(nodeRefs.contains(nodeRef));
        }

        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        results = list(parentNodeRef, -1, -1, 0);
        assertFalse(results.hasMoreItems());

        nodeRefs = results.getPage();

        for (NodeRef nodeRef : permHits) {
            assertTrue(nodeRefs.contains(nodeRef));
        }

        for (NodeRef nodeRef : permMisses) {
            assertTrue(nodeRefs.contains(nodeRef));
        }
    }

    public void testPatterns() throws Exception {
        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        NodeRef parentNodeRef = nodeService.createNode(repositoryHelper.getCompanyHome(),
                ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, getName()),
                ContentModel.TYPE_FOLDER, null).getChildRef();

        // set up some nodes to test patterns
        NodeRef nodeRef1 = createContent(parentNodeRef, "page.component-1-2.user~admin~dashboard.xml",
                ContentModel.TYPE_CONTENT);
        NodeRef nodeRef2 = createContent(parentNodeRef, "page.component-1-4.user~admin~dashboard.xml",
                ContentModel.TYPE_CONTENT);
        NodeRef nodeRef3 = createContent(parentNodeRef, "page.xml", ContentModel.TYPE_CONTENT);
        NodeRef nodeRef4 = createContent(parentNodeRef, "page.component-1-4.user~admin~panel.xml",
                ContentModel.TYPE_CONTENT);
        NodeRef nodeRef5 = createContent(parentNodeRef, "page.component-1-4.user~admin~%dashboard.xml",
                ContentModel.TYPE_CONTENT);

        AuthenticationUtil.popAuthentication();

        String pattern = "page.*.user~admin~%dashboard.xml";
        PagingResults<NodeRef> results = list(parentNodeRef, -1, -1, 0, pattern, null);
        assertFalse(results.hasMoreItems());

        int totalCnt = results.getPage().size();
        assertTrue(totalCnt == 1);
        assertEquals(nodeRef5, results.getPage().get(0));

        pattern = "page.*.user~admin~dashboard.xml";
        results = list(parentNodeRef, -1, -1, 0, pattern, null);
        assertFalse(results.hasMoreItems());

        totalCnt = results.getPage().size();
        assertTrue(totalCnt == 2);
        assertTrue(results.getPage().contains(nodeRef1));
        assertTrue(results.getPage().contains(nodeRef2));

        pattern = "*";
        results = list(parentNodeRef, -1, -1, 0, pattern, null);
        assertFalse(results.hasMoreItems());
        totalCnt = results.getPage().size();
        assertTrue(totalCnt == 5);
        assertTrue(results.getPage().contains(nodeRef1));
        assertTrue(results.getPage().contains(nodeRef2));
        assertTrue(results.getPage().contains(nodeRef3));
        assertTrue(results.getPage().contains(nodeRef4));
        assertTrue(results.getPage().contains(nodeRef5));

        pattern = "foo*bar";
        results = list(parentNodeRef, -1, -1, 0, pattern, null);
        assertFalse(results.hasMoreItems());
        totalCnt = results.getPage().size();
        assertEquals("", 0, totalCnt);

        pattern = "page.*.admin~dashboard.xml";
        results = list(parentNodeRef, -1, -1, 0, pattern, null);
        assertFalse(results.hasMoreItems());
        totalCnt = results.getPage().size();
        assertEquals("", 0, totalCnt);

        pattern = "page.*.user~admin~*.xml";
        results = list(parentNodeRef, -1, -1, 0, pattern, null);
        assertFalse(results.hasMoreItems());
        totalCnt = results.getPage().size();
        assertEquals("", 4, totalCnt);

        pattern = "page.*.user~admin~%*.xml";
        results = list(parentNodeRef, -1, -1, 0, pattern, null);
        assertFalse(results.hasMoreItems());
        totalCnt = results.getPage().size();
        assertEquals("", 1, totalCnt);

        pattern = "page.*.user~admin~%dashboard.xml";
        results = list(parentNodeRef, -1, -1, 0, pattern, null);
        assertFalse(results.hasMoreItems());
        totalCnt = results.getPage().size();
        assertEquals("", 1, totalCnt);

        pattern = "page.component-1-4.user~admin~%dashboard.xml";
        results = list(parentNodeRef, -1, -1, 0, pattern, null);
        assertFalse(results.hasMoreItems());
        totalCnt = results.getPage().size();
        assertEquals("", 1, totalCnt);

        pattern = "page.%.user~admin~%.xml";
        results = list(parentNodeRef, -1, -1, 0, pattern, null);
        assertFalse(results.hasMoreItems());
        totalCnt = results.getPage().size();
        assertEquals("", 0, totalCnt);
    }

    public void testRestrictByAssocType() throws Exception {
        NodeRef parentNodeRef = getOrCreateParentTestFolder("GetChildrenCannedQueryTest-testFolder-" + TEST_RUN_ID);

        Set<QName> assocTypeQNames = new HashSet<QName>(3);
        Set<QName> childTypeQNames = new HashSet<QName>(3);

        assocTypeQNames.clear();
        assocTypeQNames.add(ContentModel.ASSOC_CONTAINS);
        childTypeQNames.clear();
        childTypeQNames.add(ContentModel.TYPE_CONTENT);
        List<NodeRef> children = filterByAssocTypeAndCheck(parentNodeRef, assocTypeQNames, childTypeQNames);
        assertEquals(1, children.size());

        assocTypeQNames.clear();
        assocTypeQNames.add(QName.createQName("http://www.alfresco.org/test/getchildrentest/1.0", "contains1"));
        childTypeQNames.clear();
        childTypeQNames.add(ContentModel.TYPE_CONTENT);
        children = filterByAssocTypeAndCheck(parentNodeRef, assocTypeQNames, childTypeQNames);
        assertEquals(1, children.size());

        assocTypeQNames.clear();
        assocTypeQNames.add(QName.createQName("http://www.alfresco.org/test/getchildrentest/1.0", "contains1"));
        assocTypeQNames.add(ContentModel.ASSOC_CONTAINS);
        childTypeQNames.clear();
        childTypeQNames.add(ContentModel.TYPE_CONTENT);
        children = filterByAssocTypeAndCheck(parentNodeRef, assocTypeQNames, childTypeQNames);
        assertEquals(2, children.size());
    }

    @SuppressWarnings("unused")
    public void testAspectFiltering() throws Exception {
        NodeRef parentNodeRef = repositoryHelper.getCompanyHome();
        NodeRef nodeRef1 = null;
        NodeRef nodeRef2 = null;
        NodeRef nodeRef3 = null;
        NodeRef nodeRef4 = null;
        NodeRef nodeRef5 = null;
        NodeRef nodeRef6 = null;
        NodeRef nodeRef7 = null;
        NodeRef nodeRef8 = null;
        NodeRef nodeRef9 = null;
        NodeRef nodeRef10 = null;

        // Create some nodes with integer properties on which to sort, in this case cm:fiveStarRatingScheme and cm:likesRatingScheme
        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
        try {
            nodeRef1 = createContent(parentNodeRef, "node1", ContentModel.TYPE_CONTENT);
            nodeRef2 = createContent(parentNodeRef, "node2", ContentModel.TYPE_CONTENT);
            nodeRef3 = createContent(parentNodeRef, "node3", ContentModel.TYPE_CONTENT);
            nodeRef4 = createContent(parentNodeRef, "node4", ContentModel.TYPE_CONTENT);
            nodeRef5 = createContent(parentNodeRef, "node5", ContentModel.TYPE_CONTENT);

            nodeRef6 = createContent(parentNodeRef, "node6", ContentModel.TYPE_CONTENT);
            nodeRef7 = createContent(parentNodeRef, "node7", ContentModel.TYPE_CONTENT);
            nodeRef8 = createContent(parentNodeRef, "node8", ContentModel.TYPE_CONTENT);
            nodeRef9 = createContent(parentNodeRef, "node9", ContentModel.TYPE_CONTENT);
            nodeRef10 = createContent(parentNodeRef, "node10", ContentModel.TYPE_CONTENT);

            Map<QName, Serializable> props = Collections.emptyMap();

            nodeService.addAspect(nodeRef2, ContentModel.ASPECT_CLASSIFIABLE, props);
            nodeService.addAspect(nodeRef4, ContentModel.ASPECT_CLASSIFIABLE, props);
            nodeService.addAspect(nodeRef4, RenditionModel.ASPECT_RENDITION, props);
            nodeService.addAspect(nodeRef8, ContentModel.ASPECT_CLASSIFIABLE, props);
        } finally {
            AuthenticationUtil.popAuthentication();
        }

        // do children canned query
        PagingResults<NodeRef> results = list(parentNodeRef, -1, -1, 0);
        List<NodeRef> nodeRefs = results.getPage();
        for (NodeRef nodeRef : nodeRefs) {
            System.out.print(nodeRef);
            Set<QName> aspects = nodeService.getAspects(nodeRef);
            for (QName aspect : aspects) {
                System.out.print(" " + aspect);
            }
            System.out.println();
        }
        Set<QName> includedAspects = new HashSet<QName>(
                Arrays.asList(new QName[] { ContentModel.ASPECT_CLASSIFIABLE }));
        results = list(parentNodeRef, includedAspects, null, -1, -1, 0);
        assertEquals(3, results.getPage().size());

        Set<QName> excludedAspects = new HashSet<QName>(
                Arrays.asList(new QName[] { RenditionModel.ASPECT_RENDITION }));
        results = list(parentNodeRef, null, excludedAspects, -1, -1, 0);
        for (NodeRef result : results.getPage()) {
            assertFalse(nodeService.getAspects(result).contains(RenditionModel.ASPECT_RENDITION));
        }

        results = list(parentNodeRef, includedAspects, excludedAspects, -1, -1, 0);
        assertEquals(2, results.getPage().size());
    }

    // test helper method - optional filtering/sorting
    private PagingResults<NodeRef> list(NodeRef parentNodeRef, final int skipCount, final int maxItems,
            final int requestTotalCountMax, String pattern, List<Pair<QName, Boolean>> sortProps) {
        PagingRequest pagingRequest = new PagingRequest(skipCount, maxItems, null);
        pagingRequest.setRequestTotalCountMax(requestTotalCountMax);

        // get canned query
        GetChildrenCannedQueryFactory getChildrenCannedQueryFactory = (GetChildrenCannedQueryFactory) cannedQueryRegistry
                .getNamedObject(CQ_FACTORY_NAME);
        final GetChildrenCannedQuery cq = (GetChildrenCannedQuery) getChildrenCannedQueryFactory
                .getCannedQuery(parentNodeRef, pattern, null, null, null, null, null, sortProps, pagingRequest);

        // execute canned query
        RetryingTransactionCallback<CannedQueryResults<NodeRef>> callback = new RetryingTransactionCallback<CannedQueryResults<NodeRef>>() {
            @Override
            public CannedQueryResults<NodeRef> execute() throws Throwable {
                return cq.execute();
            }
        };
        CannedQueryResults<NodeRef> results = transactionService.getRetryingTransactionHelper()
                .doInTransaction(callback, true);

        List<NodeRef> nodeRefs = results.getPages().get(0);

        Integer totalCount = null;
        if (requestTotalCountMax > 0) {
            totalCount = results.getTotalResultCount().getFirst();
        }

        return new PagingNodeRefResultsImpl(nodeRefs, results.hasMoreItems(), totalCount, false);
    }

    private void filterByTypeAndCheck(NodeRef parentNodeRef, Set<QName> childTypeQNames,
            Set<QName> antiChildTypeQNames) {
        // belts-and-braces
        for (QName childTypeQName : childTypeQNames) {
            assertFalse(antiChildTypeQNames.contains(childTypeQName));
        }

        PagingResults<NodeRef> results = list(parentNodeRef, -1, -1, 0, childTypeQNames, null, null);
        assertTrue(results.getPage().size() > 0);

        PagingResults<NodeRef> antiResults = list(parentNodeRef, -1, -1, 0, antiChildTypeQNames, null, null);
        assertTrue(antiResults.getPage().size() > 0);

        List<NodeRef> childNodeRefs = results.getPage();
        List<NodeRef> antiChildNodeRefs = antiResults.getPage();

        for (NodeRef childNodeRef : childNodeRefs) {
            assertFalse(antiChildNodeRefs.contains(childNodeRef));
        }

        for (NodeRef childNodeRef : childNodeRefs) {
            QName childNodeTypeQName = nodeService.getType(childNodeRef);
            assertTrue(childTypeQNames.contains(childNodeTypeQName));
        }

        for (NodeRef childNodeRef : antiChildNodeRefs) {
            QName childNodeTypeQName = nodeService.getType(childNodeRef);
            assertTrue(antiChildTypeQNames.contains(childNodeTypeQName));
        }
    }

    private List<NodeRef> filterByAssocTypeAndCheck(NodeRef parentNodeRef, Set<QName> assocTypeQNames,
            Set<QName> childTypeQNames) {
        PagingResults<NodeRef> results = list(parentNodeRef, -1, -1, 0, assocTypeQNames, childTypeQNames, null,
                null, null, null);
        assertTrue(results.getPage().size() > 0);

        List<NodeRef> childNodeRefs = results.getPage();
        return childNodeRefs;
    }

    private void filterByPropAndCheck(NodeRef parentNodeRef, QName filterPropQName, String filterVal,
            FilterTypeString filterType, int expectedCount) {
        FilterProp filter = new FilterPropString(filterPropQName, filterVal, filterType);
        List<FilterProp> filterProps = new ArrayList<FilterProp>(1);
        filterProps.add(filter);

        // note: currently inclusive filter
        PagingResults<NodeRef> results = list(parentNodeRef, -1, -1, 0, null, filterProps, null);

        int count = results.getPage().size();
        assertEquals(expectedCount, count);

        if (logger.isInfoEnabled()) {
            logger.info("testFiltering: " + count + " items [" + filterPropQName + "," + filterVal + ","
                    + filterType + "]");
        }

        for (NodeRef nodeRef : results.getPage()) {
            Serializable propVal = nodeService.getProperty(nodeRef, filterPropQName);

            if (logger.isInfoEnabled()) {
                logger.info("testFiltering:     [" + nodeRef + "," + propVal + "]");
            }

            if (propVal instanceof String) {
                String val = (String) propVal;
                switch (filterType) {
                case STARTSWITH:
                    if (!val.startsWith(filterVal)) {
                        fail("Unexpected val: " + val + " (does not 'startWith': " + filterVal + ")");
                    }
                    break;
                case STARTSWITH_IGNORECASE:
                    if (!val.toLowerCase().startsWith(filterVal.toLowerCase())) {
                        fail("Unexpected val: " + val + " (does not 'startWithIgnoreCase': " + filterVal + ")");
                    }
                    break;
                case EQUALS:
                    if (!val.equals(filterVal)) {
                        fail("Unexpected val: " + val + " (does not 'equal': " + filterVal + ")");
                    }
                    break;
                case EQUALS_IGNORECASE:
                    if (!val.equalsIgnoreCase(filterVal)) {
                        fail("Unexpected val: " + val + " (does not 'equalIgnoreCase': " + filterVal + ")");
                    }
                    break;
                default:
                }
            } else {
                fail("Unsupported filter type: " + propVal.getClass().getName());
            }
        }
    }

    private void sortAndCheck(NodeRef parentNodeRef, QName sortPropQName, boolean sortAscending) {
        List<Pair<QName, Boolean>> sortPairs = new ArrayList<Pair<QName, Boolean>>(1);
        sortPairs.add(new Pair<QName, Boolean>(sortPropQName, sortAscending));

        PagingResults<NodeRef> results = list(parentNodeRef, -1, -1, 0, null, null, sortPairs);

        int count = results.getPage().size();
        assertTrue(count > 3);

        if (logger.isInfoEnabled()) {
            logger.info("testSorting: " + count + " items [" + sortPropQName + ","
                    + (sortAscending ? " ascending" : " descending") + "]");
        }

        Collator collator = AlfrescoCollator.getInstance(I18NUtil.getContentLocale());

        // check order
        Serializable prevVal = null;
        NodeRef prevNodeRef = null;
        int currentIteration = 0;

        boolean allValsNull = true;

        for (NodeRef nodeRef : results.getPage()) {
            currentIteration++;

            Serializable val = null;

            if (sortPropQName.equals(GetChildrenCannedQuery.SORT_QNAME_CONTENT_SIZE)
                    || sortPropQName.equals(GetChildrenCannedQuery.SORT_QNAME_CONTENT_MIMETYPE)) {
                // content data properties (size or mimetype)
                ContentData cd = (ContentData) nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT);
                if (cd != null) {
                    if (sortPropQName.equals(GetChildrenCannedQuery.SORT_QNAME_CONTENT_SIZE)) {
                        val = cd.getSize();
                    } else if (sortPropQName.equals(GetChildrenCannedQuery.SORT_QNAME_CONTENT_MIMETYPE)) {
                        val = cd.getMimetype();
                    }
                }
            } else if (sortPropQName.equals(GetChildrenCannedQuery.SORT_QNAME_NODE_TYPE)) {
                val = nodeService.getType(nodeRef);
            } else if (sortPropQName.equals(GetChildrenCannedQuery.SORT_QNAME_NODE_IS_FOLDER)) {
                val = dictionaryService.isSubClass(nodeService.getType(nodeRef), ContentModel.TYPE_FOLDER);
            } else {
                val = nodeService.getProperty(nodeRef, sortPropQName);
            }

            if (logger.isInfoEnabled()) {
                logger.info("testSorting:     [" + nodeRef + ", " + val + "]");
            }

            int result = 0;

            if (val != null) {
                allValsNull = false;
            }

            if (prevVal == null) {
                result = (val == null ? 0 : 1);
            } else if (val == null) {
                result = -1;
            } else {
                if (val instanceof Date) {
                    result = ((Date) val).compareTo((Date) prevVal);
                } else if (val instanceof String) {
                    result = collator.compare((String) val, (String) prevVal);
                } else if (val instanceof Long) {
                    result = ((Long) val).compareTo((Long) prevVal);
                } else if (val instanceof Integer) {
                    result = ((Integer) val).compareTo((Integer) prevVal);
                } else if (val instanceof QName) {
                    result = ((QName) val).compareTo((QName) prevVal);
                } else if (val instanceof Boolean) {
                    result = ((Boolean) val).compareTo((Boolean) prevVal);
                } else {
                    fail("Unsupported sort type (" + nodeRef + "): " + val.getClass().getName());
                }

                String prevName = (String) nodeService.getProperty(prevNodeRef, ContentModel.PROP_NAME);
                String currName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);

                if (!sortAscending) {
                    assertTrue("Not descending: " + result + "\n" + "   Iteration: " + currentIteration + " out of "
                            + count + "\n" + "   Previous:  " + prevNodeRef + " had " + prevVal + " (" + prevName
                            + ")\n" + "   Current :  " + nodeRef + " had " + val + " (" + currName + ")",
                            result <= 0);
                } else {
                    assertTrue("Not ascending: " + result + "\n" + "   Iteration: " + currentIteration + " out of "
                            + count + "\n" + "   Previous:  " + prevNodeRef + " had " + prevVal + " (" + prevName
                            + ")\n" + "   Current :  " + nodeRef + " had " + val + " (" + currName + ")",
                            result >= 0);
                }
            }
            prevVal = val;
            prevNodeRef = nodeRef;
        }

        assertFalse("All values were null", allValsNull);
    }

    // test helper method - no filtering/sorting
    private PagingResults<NodeRef> list(NodeRef parentNodeRef, Set<QName> inclusiveAspects,
            Set<QName> exclusiveAscpects, final int skipCount, final int maxItems, final int requestTotalCountMax) {
        return list(parentNodeRef, skipCount, maxItems, requestTotalCountMax, null, null, null, null,
                inclusiveAspects, exclusiveAscpects);
    }

    // test helper method - no filtering/sorting
    private PagingResults<NodeRef> list(NodeRef parentNodeRef, final int skipCount, final int maxItems,
            final int requestTotalCountMax) {
        return list(parentNodeRef, skipCount, maxItems, requestTotalCountMax, null, null, null, null, null, null);
    }

    // test helper method - optional filtering/sorting
    private PagingResults<NodeRef> list(NodeRef parentNodeRef, final int skipCount, final int maxItems,
            final int requestTotalCountMax, Set<QName> childTypeQNames, List<FilterProp> filterProps,
            List<Pair<QName, Boolean>> sortProps) {
        PagingRequest pagingRequest = new PagingRequest(skipCount, maxItems, null);
        pagingRequest.setRequestTotalCountMax(requestTotalCountMax);

        // get canned query (note: test the fileFolder extension - including support for sorting folders first)
        GetChildrenCannedQueryFactory getChildrenCannedQueryFactory = (GetChildrenCannedQueryFactory) cannedQueryRegistry
                .getNamedObject(CQ_FACTORY_NAME);
        final GetChildrenCannedQuery cq = (GetChildrenCannedQuery) getChildrenCannedQueryFactory.getCannedQuery(
                parentNodeRef, null, null, childTypeQNames, null, null, filterProps, sortProps, pagingRequest);

        // execute canned query
        RetryingTransactionCallback<CannedQueryResults<NodeRef>> callback = new RetryingTransactionCallback<CannedQueryResults<NodeRef>>() {
            @Override
            public CannedQueryResults<NodeRef> execute() throws Throwable {
                return cq.execute();
            }
        };
        CannedQueryResults<NodeRef> results = transactionService.getRetryingTransactionHelper()
                .doInTransaction(callback, true);

        List<NodeRef> nodeRefs = results.getPages().get(0);

        Integer totalCount = null;
        if (requestTotalCountMax > 0) {
            totalCount = results.getTotalResultCount().getFirst();
        }

        return new PagingNodeRefResultsImpl(nodeRefs, results.hasMoreItems(), totalCount, false);
    }

    // test helper method - optional filtering/sorting
    private PagingResults<NodeRef> list(NodeRef parentNodeRef, final int skipCount, final int maxItems,
            final int requestTotalCountMax, Set<QName> assocTypeQNames, Set<QName> childTypeQNames,
            List<FilterProp> filterProps, List<Pair<QName, Boolean>> sortProps, Set<QName> inclusiveAspects,
            Set<QName> exclusiveAspects) {
        PagingRequest pagingRequest = new PagingRequest(skipCount, maxItems, null);
        pagingRequest.setRequestTotalCountMax(requestTotalCountMax);

        // get canned query
        GetChildrenCannedQueryFactory getChildrenCannedQueryFactory = (GetChildrenCannedQueryFactory) cannedQueryRegistry
                .getNamedObject(CQ_FACTORY_NAME);
        final GetChildrenCannedQuery cq = (GetChildrenCannedQuery) getChildrenCannedQueryFactory.getCannedQuery(
                parentNodeRef, null, assocTypeQNames, childTypeQNames, inclusiveAspects, exclusiveAspects,
                filterProps, sortProps, pagingRequest);

        // execute canned query
        RetryingTransactionCallback<CannedQueryResults<NodeRef>> callback = new RetryingTransactionCallback<CannedQueryResults<NodeRef>>() {
            @Override
            public CannedQueryResults<NodeRef> execute() throws Throwable {
                return cq.execute();
            }
        };
        CannedQueryResults<NodeRef> results = transactionService.getRetryingTransactionHelper()
                .doInTransaction(callback, true);

        List<NodeRef> nodeRefs = results.getPages().get(0);

        Integer totalCount = null;
        if (requestTotalCountMax > 0) {
            totalCount = results.getTotalResultCount().getFirst();
        }

        return new PagingNodeRefResultsImpl(nodeRefs, results.hasMoreItems(), totalCount, false);
    }

    private class PagingNodeRefResultsImpl implements PagingResults<NodeRef> {
        private List<NodeRef> nodeRefs;

        private boolean hasMorePages;

        private Integer totalResultCount; // null => not requested (or unknown)
        private Boolean isTotalResultCountCutoff; // null => unknown

        public PagingNodeRefResultsImpl(List<NodeRef> nodeRefs, boolean hasMorePages, Integer totalResultCount,
                Boolean isTotalResultCountCutoff) {
            this.nodeRefs = nodeRefs;
            this.hasMorePages = hasMorePages;
            this.totalResultCount = totalResultCount;
            this.isTotalResultCountCutoff = isTotalResultCountCutoff;
        }

        public List<NodeRef> getPage() {
            return nodeRefs;
        }

        public boolean hasMoreItems() {
            return hasMorePages;
        }

        public Pair<Integer, Integer> getTotalResultCount() {
            return new Pair<Integer, Integer>(totalResultCount,
                    (isTotalResultCountCutoff ? null : totalResultCount));
        }

        public String getQueryExecutionId() {
            return "";
        }
    }

    private NodeRef createFolder(NodeRef parentNodeRef, String folderName, QName folderType) throws IOException {
        Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
        properties.put(ContentModel.PROP_NAME, folderName);

        NodeRef nodeRef = nodeService.getChildByName(parentNodeRef, ContentModel.ASSOC_CONTAINS, folderName);
        if (nodeRef != null) {
            nodeService.deleteNode(nodeRef);
        }

        nodeRef = nodeService.createNode(parentNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName(folderName),
                folderType, properties).getChildRef();
        return nodeRef;
    }

    private NodeRef createContent(NodeRef parentNodeRef, String fileName, QName contentType) throws IOException {
        Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
        properties.put(ContentModel.PROP_NAME, fileName);
        properties.put(ContentModel.PROP_TITLE, fileName + " my title");
        properties.put(ContentModel.PROP_DESCRIPTION, fileName + " my description");

        NodeRef nodeRef = nodeService.getChildByName(parentNodeRef, ContentModel.ASSOC_CONTAINS, fileName);
        if (nodeRef != null) {
            nodeService.deleteNode(nodeRef);
        }

        nodeRef = nodeService.createNode(parentNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName(fileName),
                contentType, properties).getChildRef();

        ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
        writer.setMimetype(mimetypeService.guessMimetype(fileName));
        writer.putContent("my text content");

        return nodeRef;
    }

    private NodeRef createContent(NodeRef parentNodeRef, QName childAssocType, String fileName, QName contentType)
            throws IOException {
        Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
        properties.put(ContentModel.PROP_NAME, fileName);
        properties.put(ContentModel.PROP_TITLE, fileName + " my title");
        properties.put(ContentModel.PROP_DESCRIPTION, fileName + " my description");

        NodeRef nodeRef = nodeService.getChildByName(parentNodeRef, childAssocType, fileName);
        if (nodeRef != null) {
            nodeService.deleteNode(nodeRef);
        }

        nodeRef = nodeService
                .createNode(parentNodeRef, childAssocType, QName.createQName(fileName), contentType, properties)
                .getChildRef();

        ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
        writer.setMimetype(mimetypeService.guessMimetype(fileName));
        writer.putContent("my text content");

        return nodeRef;
    }

    private void loadContent(NodeRef parentNodeRef, String inFileName, String title, String description,
            boolean readAllowed, Set<NodeRef> results) throws IOException {
        String newFileName = TEST_FILE_PREFIX + inFileName;

        Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
        properties.put(ContentModel.PROP_NAME, newFileName);
        properties.put(ContentModel.PROP_TITLE, title);
        properties.put(ContentModel.PROP_DESCRIPTION, description);

        NodeRef nodeRef = nodeService.createNode(parentNodeRef, ContentModel.ASSOC_CONTAINS,
                QName.createQName(newFileName), ContentModel.TYPE_CONTENT, properties).getChildRef();

        String classPath = "quick/" + inFileName;
        File file = null;
        URL url = getClass().getClassLoader().getResource(classPath);
        if (url != null) {
            file = new File(url.getFile());
            if (!file.exists()) {
                file = null;
            }
        }

        if (file == null) {
            fail("Unable to find test file: " + classPath);
        }

        ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
        writer.setMimetype(mimetypeService.guessMimetype(inFileName));
        writer.putContent(file);

        if (!readAllowed) {
            // deny read (by explicitly breaking inheritance)
            permissionService.setInheritParentPermissions(nodeRef, false);
        }

        results.add(nodeRef);
    }

    private void createUser(String userName, String firstName, String lastName) {
        if (!authenticationService.authenticationExists(userName)) {
            authenticationService.createAuthentication(userName, "PWD".toCharArray());
        }

        if (!personService.personExists(userName)) {
            PropertyMap ppOne = new PropertyMap(5);
            ppOne.put(ContentModel.PROP_USERNAME, userName);
            ppOne.put(ContentModel.PROP_FIRSTNAME, firstName);
            ppOne.put(ContentModel.PROP_LASTNAME, lastName);
            ppOne.put(ContentModel.PROP_EMAIL, "email@email.com");
            ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle");

            personService.createPerson(ppOne);
        }
    }
}