falcon-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rag...@apache.org
Subject falcon git commit: FALCON-1171: Adding search API tests contributed by Paul Isaychuk
Date Thu, 23 Apr 2015 17:31:56 GMT
Repository: falcon
Updated Branches:
  refs/heads/master cecc3b427 -> 0232f1b6b


FALCON-1171: Adding search API tests contributed by Paul Isaychuk


Project: http://git-wip-us.apache.org/repos/asf/falcon/repo
Commit: http://git-wip-us.apache.org/repos/asf/falcon/commit/0232f1b6
Tree: http://git-wip-us.apache.org/repos/asf/falcon/tree/0232f1b6
Diff: http://git-wip-us.apache.org/repos/asf/falcon/diff/0232f1b6

Branch: refs/heads/master
Commit: 0232f1b6bddfc16762357cd0f70b9b89de0c308a
Parents: cecc3b4
Author: Raghav Kumar Gautam <raghav@apache.org>
Authored: Thu Apr 23 10:31:48 2015 -0700
Committer: Raghav Kumar Gautam <raghav@apache.org>
Committed: Thu Apr 23 10:31:48 2015 -0700

----------------------------------------------------------------------
 falcon-regression/CHANGES.txt                   |   1 +
 .../helpers/entity/AbstractEntityHelper.java    |  14 +-
 .../apache/falcon/regression/SearchApiTest.java | 497 +++++++++++++++++++
 .../regression/entity/ListEntitiesTest.java     |   6 +-
 4 files changed, 509 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/falcon/blob/0232f1b6/falcon-regression/CHANGES.txt
----------------------------------------------------------------------
diff --git a/falcon-regression/CHANGES.txt b/falcon-regression/CHANGES.txt
index 7b56be8..104c01f 100644
--- a/falcon-regression/CHANGES.txt
+++ b/falcon-regression/CHANGES.txt
@@ -5,6 +5,7 @@ Trunk (Unreleased)
   INCOMPATIBLE CHANGES
 
   NEW FEATURES
+   FALCON-1171 Adding search API tests (Paul Isaychuk via Raghav Kumar Gautam)
 
    FALCON-1167 Homepage & Login test for search-ui (Raghav Kumar Gautam via Ruslan Ostafiychuk)
 

http://git-wip-us.apache.org/repos/asf/falcon/blob/0232f1b6/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/core/helpers/entity/AbstractEntityHelper.java
----------------------------------------------------------------------
diff --git a/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/core/helpers/entity/AbstractEntityHelper.java
b/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/core/helpers/entity/AbstractEntityHelper.java
index b899f1a..08e11a1 100644
--- a/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/core/helpers/entity/AbstractEntityHelper.java
+++ b/falcon-regression/merlin-core/src/main/java/org/apache/falcon/regression/core/helpers/entity/AbstractEntityHelper.java
@@ -252,14 +252,16 @@ public abstract class AbstractEntityHelper {
 
     public ServiceResponse listEntities()
         throws IOException, URISyntaxException, AuthenticationException, InterruptedException
{
-        return listEntities(null, null);
+        return listEntities(null, null, null);
     }
 
-    public ServiceResponse listEntities(String params, String user)
+    public ServiceResponse listEntities(String entityType, String params, String user)
         throws IOException, URISyntaxException, AuthenticationException, InterruptedException
{
-        LOGGER.info("fetching " + getEntityType() + " list");
-        String url = createUrl(this.hostname + URLS.LIST_URL.getValue(),
-            getEntityType() + colo);
+        if (StringUtils.isEmpty(entityType)) {
+            entityType = getEntityType();
+        }
+        LOGGER.info("fetching " + entityType + " list");
+        String url = createUrl(this.hostname + URLS.LIST_URL.getValue(), entityType + colo);
         if (StringUtils.isNotEmpty(params)){
             url += colo.isEmpty() ? "?" + params : "&" + params;
         }
@@ -268,7 +270,7 @@ public abstract class AbstractEntityHelper {
 
     public ServiceResponse listAllEntities(String params, String user)
         throws AuthenticationException, IOException, URISyntaxException, InterruptedException
{
-        return listEntities((params == null ? "" : params + '&')
+        return listEntities(null, (params == null ? "" : params + '&')
             + "numResults=" + Integer.MAX_VALUE, user);
     }
 

http://git-wip-us.apache.org/repos/asf/falcon/blob/0232f1b6/falcon-regression/merlin/src/test/java/org/apache/falcon/regression/SearchApiTest.java
----------------------------------------------------------------------
diff --git a/falcon-regression/merlin/src/test/java/org/apache/falcon/regression/SearchApiTest.java
b/falcon-regression/merlin/src/test/java/org/apache/falcon/regression/SearchApiTest.java
new file mode 100644
index 0000000..b317944
--- /dev/null
+++ b/falcon-regression/merlin/src/test/java/org/apache/falcon/regression/SearchApiTest.java
@@ -0,0 +1,497 @@
+/**
+ * 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.falcon.regression;
+
+import com.google.common.collect.Ordering;
+import org.apache.falcon.regression.Entities.FeedMerlin;
+import org.apache.falcon.regression.Entities.ProcessMerlin;
+import org.apache.falcon.regression.core.bundle.Bundle;
+import org.apache.falcon.regression.core.helpers.ColoHelper;
+import org.apache.falcon.regression.core.response.ServiceResponse;
+import org.apache.falcon.regression.core.util.AssertUtil;
+import org.apache.falcon.regression.core.util.BundleUtil;
+import org.apache.falcon.regression.core.util.OSUtil;
+import org.apache.falcon.regression.testHelper.BaseTestClass;
+import org.apache.falcon.resource.EntityList.EntityElement;
+import org.apache.hadoop.security.authentication.client.AuthenticationException;
+import org.apache.log4j.Logger;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import javax.xml.bind.JAXBException;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.ListIterator;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * Search api tests.
+ */
+@Test(groups = "search-api")
+public class SearchApiTest extends BaseTestClass {
+
+    private static final Logger LOGGER = Logger.getLogger(SearchApiTest.class);
+    private ColoHelper cluster = servers.get(0);
+    private String baseTestHDFSDir = cleanAndGetTestDir();
+    private String aggregateWorkflowDir = baseTestHDFSDir + "/aggregator";
+    private final String base = getClass().getSimpleName();
+    private static final Comparator<EntityElement> ASC = new Comparator<EntityElement>()
{
+        @Override
+        public int compare(EntityElement o1, EntityElement o2) {
+            return o1.name.compareTo(o2.name);
+        }
+    };
+    private static final Comparator<EntityElement> DESC = Collections.reverseOrder(ASC);
+
+    /**
+     * Method creates a set of entities.
+     * |--------------------------------------------+-----------------------------+-----------------------------|
+     * | Basic input/output Feeds (without tags)    | bundle0-input-feed          | bundle0-output-feed
        |
+     * |--------------+-----------------------------+-----------------------------+-----------------------------|
+     * | Feed Name    | bundle1-feed                | bundle2-feed                | bundle3-feed
               |
+     * | Tags         | specific=bundle1            | specific=bundle2            | specific=bundle3
           |
+     * |              | common=common               | common=common               | common=common
              |
+     * |              | partial=b1b2                | partial=b1b2                |     
                       |
+     * |--------------+-----------------------------+-----------------------------+-----------------------------|
+     * | Process Name | bundle1-process             | bundle2-process             | bundle3-process
            |
+     * | Tags         | specific=bundle1            | specific=bundle2            | specific=bundle3
           |
+     * |              | partial=b1b2                | partial=b1b2                | common=common
              |
+     * |              | common=common               | common=common               |     
                       |
+     * |--------------+-----------------------------+-----------------------------+-----------------------------|
+     * | Mirror Name  | bundle1-mirror-process      | bundle2-mirror-process      | bundle3-mirror-process
     |
+     * | Tags         | specific=bundle1            | specific=bundle2            | specific=bundle3
           |
+     * |              | common=common               | common=common               | common=common
              |
+     * |              | partial=b1b2                | partial=b1b2                | _falcon_mirroring_type=HDFS
|
+     * |              | _falcon_mirroring_type=HDFS | _falcon_mirroring_type=HIVE |     
                       |
+     * |--------------+-----------------------------+-----------------------------+-----------------------------|
+     */
+    @BeforeClass(alwaysRun = true)
+    public void prepareData()
+        throws IOException, URISyntaxException, AuthenticationException, InterruptedException,
JAXBException {
+        uploadDirToClusters(aggregateWorkflowDir, OSUtil.RESOURCES_OOZIE);
+
+        /* Prepare bundle template*/
+        bundles[0] = BundleUtil.readELBundle();
+        bundles[0] = new Bundle(bundles[0], servers.get(0));
+        bundles[0].generateUniqueBundle(this);
+        bundles[0].submitClusters(cluster);
+        String prefix = getClass().getSimpleName() + "-bundle";
+
+        FeedMerlin basicFeed = new FeedMerlin(bundles[0].getInputFeedFromBundle());
+        basicFeed.setName(prefix + "0-input-feed");
+        AssertUtil.assertSucceeded(cluster.getFeedHelper().submitAndSchedule(basicFeed.toString()));
+        basicFeed = new FeedMerlin(bundles[0].getOutputFeedFromBundle());
+        basicFeed.setName(prefix + "0-output-feed");
+        AssertUtil.assertSucceeded(cluster.getFeedHelper().submitAndSchedule(basicFeed.toString()));
+
+        /* Submit 3 bundles of feeds */
+        FeedMerlin feed = new FeedMerlin(bundles[0].getInputFeedFromBundle());
+        for (int i = 1; i <= 3; i++) {
+            String feedName = prefix + i + "-feed";
+            feed.setName(feedName);
+            String tags = "specific=bundle" + i + ",common=common";
+            if (i <= 2) {
+                tags += ",partial=b1b2";
+            }
+            feed.setTags(tags);
+            AssertUtil.assertSucceeded(cluster.getFeedHelper().submitEntity(feed.toString()));
+        }
+
+        /* Submit 3 bundles of processes */
+        ProcessMerlin process = bundles[0].getProcessObject();
+
+        //replace input and output with feeds of bundle0
+        process.addInputFeed("input", prefix + "0-input-feed");
+        process.getInputs().getInputs().remove(0);
+        process.addOutputFeed("output", prefix + "0-output-feed");
+        process.getOutputs().getOutputs().remove(0);
+        process.setWorkflow(aggregateWorkflowDir, null, null);
+        for (int i = 1; i <= 3; i++) {
+            process.setName(prefix + i + "-process");
+            String tags = "specific=bundle" + i + ",common=common";
+            if (i <= 2) {
+                tags += ",partial=b1b2";
+            }
+            process.setTags(tags);
+            AssertUtil.assertSucceeded(cluster.getProcessHelper().submitEntity(process.toString()));
+
+            //submit a mirroring process
+            if (i % 2 == 1) {
+                tags += ",_falcon_mirroring_type=HDFS";
+            } else {
+                tags += ",_falcon_mirroring_type=HIVE";
+            }
+            process.setName(prefix + i + "-mirror-process");
+            process.setTags(tags);
+            AssertUtil.assertSucceeded(cluster.getProcessHelper().submitEntity(process.toString()));
+        }
+    }
+
+    /**
+     * Test hits API, retrieves entities and validates them based on set of expected conditions.
+     * @param params object which consists of test case parameters such as type, name sequence,
tag keys etc.
+     * @param result object which contains set of expected conditions. Performs validations
of entities.
+     */
+    @Test(dataProvider = "getSearchParams")
+    public void search(QueryParams params, Result result)
+        throws URISyntaxException, AuthenticationException, InterruptedException, IOException,
JAXBException {
+        ServiceResponse serviceResponse = cluster.getClusterHelper()
+            .listEntities(params.getType(), params.getParams(), null);
+        if (result.getExpError() != null) {
+            AssertUtil.assertFailed(serviceResponse, result.getExpError());
+        } else {
+            String order = null;
+            if (params.getParams().contains("orderBy")) {
+                order = params.getParams().contains("sortOrder=desc") ? "desc" : "asc";
+            }
+            result.validateEntities(serviceResponse.getEntityList().getElements(), order);
+        }
+    }
+
+    @DataProvider
+    private Object[][] getSearchParams() {
+        return new Object[][]{
+
+            /*Nameseq test cases*/
+            {new QueryParams().withNumResults(12), new Result().withExpBundles(0, 1, 2, 3)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(11), },
+            {new QueryParams().withNameSeq(base + "-bundle1-feed"), new Result().withExpBundles(1)
+                .withExpTypes("feed").withExpTotal(1), },
+            {new QueryParams().withNameSeq(base + "-bundle2-mirror-process"), new Result().withExpBundles(2)
+                .withExpTypes("process", "mirror").withExpTotal(1), },
+            {new QueryParams().withNameSeq(base + "-bUnDlE1-fEeD"), new Result().withExpBundles(1)
+                .withExpTypes("feed").withExpTotal(1), },
+            {new QueryParams().withNameSeq(base + "-bundle1-feed-non"), new Result(), },
+            {new QueryParams().withNameSeq(base + "-bundle2-process-non"), new Result(),
},
+            {new QueryParams().withNameSeq("-bundle2-"), new Result().withExpBundles(2)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(3), },
+            {new QueryParams().withNameSeq(base).withNumResults(12), new Result().withExpBundles(0,
1, 2, 3)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(11), },
+            {new QueryParams().withNameSeq("bundleFeed"), new Result().withExpBundles(0,
1, 2, 3)
+                .withExpTypes("feed").withExpTotal(5), },
+            {new QueryParams().withNameSeq("bundleProcess"), new Result().withExpBundles(1,
2, 3)
+                .withExpTypes("process", "mirror").withExpTotal(6), },
+            {new QueryParams().withNameSeq("bUnDlEfeEd"), new Result().withExpBundles(0,
1, 2, 3)
+                .withExpTypes("feed").withExpTotal(5), },
+            {new QueryParams().withNameSeq("bUnDlEprOCesS"), new Result().withExpBundles(1,
2, 3)
+                .withExpTypes("process", "mirror").withExpTotal(6), },
+            {new QueryParams().withNameSeq(base + "-bundle-nonexistent"), new Result(), },
+            //unusual nameseq forms
+            {new QueryParams().withNameSeq("012345"), new Result(), },
+            {new QueryParams().withNameSeq("f€€d"), new Result(), },
+            {new QueryParams().withNameSeq("_-#$@"), new Result(), },
+            {new QueryParams().withNameSeq("").withNumResults(12), new Result().withExpBundles(0,
1, 2, 3)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(11), },
+
+            /*Tagkey test cases*/
+            /* Full tagkey name*/
+            {new QueryParams().withTagkey("bundle1"), new Result().withExpBundles(1)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(3), },
+            {new QueryParams().withTagkey("bundle2"), new Result().withExpBundles(2)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(3), },
+            {new QueryParams().withTagkey("bundle3"), new Result().withExpBundles(3)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(3), },
+            {new QueryParams().withTagkey("bUnDlE1"), new Result().withExpBundles(1)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(3), },
+            {new QueryParams().withTagkey("_falcon_mirroring_type"), new Result().withExpBundles(1,
2, 3)
+                .withExpTypes("process", "mirror").withExpTotal(3), },
+            /* Special, utf-8 symbols*/
+            {new QueryParams().withTagkey("bun@le#"), new Result()},
+            {new QueryParams().withTagkey("bundl€"), new Result()},
+            /* Common for a pair of bundles; use both tag and value as tagkey*/
+            {new QueryParams().withTagkey("b1b2"), new Result().withExpBundles(1, 2)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(6), },
+            {new QueryParams().withTagkey("partial"), new Result().withExpBundles(1, 2)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(6), },
+            /* Common for 1,2,3 bundles*/
+            {new QueryParams().withTagkey("common"), new Result().withExpBundles(1, 2, 3)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(9), },
+            {new QueryParams().withTagkey("bundle"), new Result().withExpBundles(1, 2, 3)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(9), },
+            {new QueryParams().withTagkey("undle"), new Result().withExpBundles(1, 2, 3)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(9), },
+            {new QueryParams().withTagkey("undle,undle"), new Result().withExpBundles(1,
2, 3)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(9), },
+            /* Multiple full tags*/
+            {new QueryParams().withTagkey("common,bundle1"), new Result().withExpBundles(1)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(3), },
+            {new QueryParams().withTagkey("common,bundle2"), new Result().withExpBundles(2)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(3), },
+            {new QueryParams().withTagkey("common,bundle3"), new Result().withExpBundles(3)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(3), },
+            {new QueryParams().withTagkey("common,bundle1,b1b2"), new Result().withExpBundles(1)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(3), },
+            {new QueryParams().withTagkey("common,bundle2,b1b2"), new Result().withExpBundles(2)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(3), },
+            /* Multiple, diff case*/
+            {new QueryParams().withTagkey("cOmMoN,bUnDlE2,B1b2"), new Result().withExpBundles(2)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(3), },
+            /* Multiple partial tags*/
+            {new QueryParams().withTagkey("common,undle"), new Result().withExpBundles(1,
2, 3)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(9), },
+            {new QueryParams().withTagkey("ommo,undle"), new Result().withExpBundles(1, 2,
3)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(9), },
+            {new QueryParams().withTagkey("ommo,ndle1"), new Result().withExpBundles(1)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(3), },
+            {new QueryParams().withTagkey("oMMon,ndle2"), new Result().withExpBundles(2)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(3), },
+            {new QueryParams().withTagkey("oMMon,b1b"), new Result().withExpBundles(1, 2)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(6), },
+            {new QueryParams().withTagkey("comm,ndle3"), new Result().withExpBundles(3)
+                .withExpTypes("feed", "process", "mirror").withExpTotal(3), },
+            /* Non existent*/
+            {new QueryParams().withTagkey("bundle9"), new Result(), },
+            {new QueryParams().withTagkey("common,undle,zero"), new Result(), },
+
+            /*Custom filter test cases*/
+            //different types
+            {new QueryParams().forType("process"), new Result().withExpBundles(1, 2, 3)
+                .withExpTypes("process", "mirror").withExpTotal(6), },
+            {new QueryParams().forType("feed"), new Result().withExpBundles(0, 1, 2, 3)
+                .withExpTypes("feed").withExpTotal(5), },
+            {new QueryParams().forType("FEED"), new Result().withExpBundles(0, 1, 2, 3)
+                .withExpTypes("feed").withExpTotal(5), },
+            {new QueryParams().forType("MEGAFEED"), new Result()
+                .withExpError("Invalid entity type: MEGAFEED. Expected [feed, process, cluster]"),
},
+            //custom filters
+            {new QueryParams().forType("process").withNameSeq("bundle").withTagkey("bundle,b1b2").withNumResults(3)
+                .withOrder(true).withSortOrder("desc"), new Result().withExpBundles(1, 2)
+                    .withExpTypes("process", "mirror").withExpTotal(3), },
+            {new QueryParams().forType("schedulable").withNameSeq("bundle").withTagkey("partial,common")
+                .withOrder(true), new Result().withExpBundles(1, 2).withExpTypes("feed",
"process", "mirror")
+                .withExpTotal(6), },
+            {new QueryParams().forType("feed").withNameSeq("bundlefeed").withNumResults(2).withOrder(true)
+                .withSortOrder("asc"), new Result().withExpBundles(0, 1, 2, 3)
+                    .withExpTypes("feed").withExpTotal(2), },
+            {new QueryParams().forType("schedulable").withNameSeq("bundleproc").withTagkey("_falcon_mirroring_type")
+                .withOrder(true).withSortOrder("desc"), new Result().withExpBundles(1, 2,
3)
+                    .withExpTypes("process", "mirror").withExpTotal(3), },
+            //one option excludes another
+            {new QueryParams().forType("process").withNameSeq("bundlefeed"), new Result(),
},
+            {new QueryParams().forType("feed").withNameSeq("bundleprocess"), new Result(),
},
+            {new QueryParams().forType("schedulable").withNameSeq("bundle3").withTagkey("b1b2").withOrder(true),
+                new Result(), },
+        };
+    }
+
+    /**
+     * Contains all required params for search api request.
+     */
+    private class QueryParams {
+        private String type = "schedulable";
+        private String nameSeq = null;
+        private String tagKey = null;
+        private String orderBy = null;
+        private String sortOrder = null;
+        private Integer numResults = null;
+        private String fields = "&fields=tags";
+
+        public QueryParams forType(String paramType) {
+            this.type = paramType;
+            return this;
+        }
+
+        public QueryParams withNameSeq(String paramNameSeq) {
+            this.nameSeq = paramNameSeq;
+            return this;
+        }
+
+        public QueryParams withTagkey(String paramTagkey) {
+            this.tagKey = paramTagkey;
+            return this;
+        }
+
+        public QueryParams withOrder(boolean ordered) {
+            if (ordered) {
+                this.orderBy = "&orderBy=name";
+            }
+            return this;
+        }
+
+        public QueryParams withSortOrder(String paramSortOrder) {
+            this.sortOrder = paramSortOrder;
+            return this;
+        }
+
+        public QueryParams withNumResults(Integer paramNumResults) {
+            this.numResults = paramNumResults;
+            return this;
+        }
+
+        /**
+         * @return url param string.
+         */
+        public String getParams() {
+            return (nameSeq != null ? "&nameseq=" + nameSeq : "")
+                + (tagKey != null ? "&tagkey=" + tagKey : "")
+                + (numResults != null ? "&numResults=" + numResults : "")
+                + (orderBy != null ? "&orderBy=name" + orderBy : "")
+                + (sortOrder != null ? "&sortOrder=" + sortOrder : "") + fields;
+        }
+
+        public String getType() {
+            return this.type;
+        }
+
+        /**
+         * Pretty prints params to param string.
+         */
+        @Override
+        public String toString() {
+            return "[type: " + type + (nameSeq != null ? "; nseq: " + nameSeq : "")
+                + (tagKey != null ? "; tagkey: " + tagKey : "")
+                + (numResults != null ? "; n:" + numResults : "")
+                + (orderBy != null ? "; ordered" : "")
+                + (sortOrder != null ? "; " + sortOrder : "") + "]";
+        }
+    }
+
+    /**
+     * Class for entities response validation.
+     */
+    private class Result {
+        private List<String> expectedTypes = null;
+        private String expectedError = null;
+        private List<String> expectedBundles = null;
+        private int expectedTotal = 0;
+        private final List<String> allBundles = Arrays.asList("bundle0", "bundle1",
"bundle2", "bundle3");
+        private final List<String> allTypes =  Arrays.asList("feed", "process", "mirror");
+
+        public Result withExpBundles(int... bundleNums) {
+            expectedBundles = new ArrayList<>();
+            for (int bundleNum : bundleNums) {
+                expectedBundles.add("bundle" + bundleNum);
+            }
+            return this;
+        }
+
+        public Result withExpTypes(String... types) {
+            expectedTypes = Arrays.asList(types);
+            return this;
+        }
+
+        public Result withExpError(String message) {
+            this.expectedError = message;
+            return this;
+        }
+
+        public Result withExpTotal(int total) {
+            this.expectedTotal = total;
+            return this;
+        }
+
+        public String getExpError() {
+            return expectedError;
+        }
+
+        /**
+         * Pretty prints params to param string.
+         */
+        @Override
+        public String toString() {
+            if (expectedError != null) {
+                return "Expected: error.";
+            } else {
+                return String.format("[Expected: %d %s%s]", expectedTotal,
+                    (expectedTypes != null ? expectedTypes + "s": "entities"),
+                    (expectedBundles != null ? " of " + expectedBundles : ""));
+            }
+        }
+
+        /**
+         * Validates entities number and order. Checks that each entity belongs to expected
bundle and type.
+         * @param entities entities
+         * @param order expected order
+         */
+        public void validateEntities(EntityElement[] entities, String order) {
+            //validate number of entities
+            if (expectedTotal == 0) {
+                assertNull(entities, "Response shouldn't contain entities.");
+            } else {
+                List<EntityElement> entitiesList = new ArrayList<>(Arrays.asList(entities));
+                cleanUpResult(entitiesList);
+                LOGGER.info("Entities after clean up: \n" + entitiesList);
+                assertEquals(entitiesList.size(), expectedTotal, "Number of entities is not
as expected.");
+
+                //Check that each entity belongs to expected bundle and type
+                for (EntityElement entity : entitiesList) {
+                    assertTrue(matches(entity.name, allBundles, expectedBundles),
+                        entity.name + " doesn't belong to expected bundles: " + expectedBundles);
+                    assertTrue(matches(entity.name, allTypes, expectedTypes),
+                        entity.name + " doesn't belong to expected types: " + expectedTypes);
+                }
+                //check the order
+                if (order != null) {
+                    Assert.assertTrue(Ordering.from(order.equals("desc") ? DESC : ASC).isOrdered(entitiesList),
+                        String.format("Entities are not ordered in %sending order", order));
+                }
+            }
+        }
+
+        /**
+         * Checks that entity name corresponds to expected bundle and type.
+         */
+        private boolean matches(String name, List<String> all, List<String> expectedParts)
{
+            boolean matches = false;
+            for (String expectedPart : expectedParts) {
+                if (name.contains(expectedPart)) {
+                    matches = true;
+                    break;
+                }
+            }
+            List<String> unexpectedParts = new ArrayList<>(all);
+            unexpectedParts.removeAll(expectedParts);
+            for (String unexpectedPart : unexpectedParts) {
+                if (name.contains(unexpectedPart)) {
+                    matches = false;
+                    break;
+                }
+            }
+            return matches;
+        }
+    }
+
+    /**
+     * Cleans up a result list from items which belong to everything else than current test
class.
+     * @param entityElements result array
+     */
+    private void cleanUpResult(List<EntityElement> entityElements) {
+        for (ListIterator<EntityElement> iterator = entityElements.listIterator();
iterator.hasNext();) {
+            String name = iterator.next().name;
+            if (!name.contains(base)) {
+                iterator.remove();
+            }
+        }
+    }
+
+    @AfterClass(alwaysRun = true)
+    public void tearDown() {
+        removeTestClassEntities();
+    }
+}

http://git-wip-us.apache.org/repos/asf/falcon/blob/0232f1b6/falcon-regression/merlin/src/test/java/org/apache/falcon/regression/entity/ListEntitiesTest.java
----------------------------------------------------------------------
diff --git a/falcon-regression/merlin/src/test/java/org/apache/falcon/regression/entity/ListEntitiesTest.java
b/falcon-regression/merlin/src/test/java/org/apache/falcon/regression/entity/ListEntitiesTest.java
index 3ae44e6..e3fcaf1 100644
--- a/falcon-regression/merlin/src/test/java/org/apache/falcon/regression/entity/ListEntitiesTest.java
+++ b/falcon-regression/merlin/src/test/java/org/apache/falcon/regression/entity/ListEntitiesTest.java
@@ -178,7 +178,7 @@ public class ListEntitiesTest extends BaseTestClass {
         for (int i = 0; i <= allEntitiesCount; i++) {
 
             EntityElement[] entities =
-                helper.listEntities("offset=" + i, null).getEntityList().getElements();
+                helper.listEntities(null, "offset=" + i, null).getEntityList().getElements();
             LOGGER.info(String.format("%s entities with offset %d: %s",
                 helper.getEntityType(), i, Arrays.toString(entities)));
 
@@ -206,7 +206,7 @@ public class ListEntitiesTest extends BaseTestClass {
 
         for (int i = 1; i <= allEntitiesCount; i++) {
             EntityElement[] entities =
-                helper.listEntities("numResults=" + i, null).getEntityList().getElements();
+                helper.listEntities(null, "numResults=" + i, null).getEntityList().getElements();
             Assert.assertEquals(entities.length, i,
                 "Number of entities is not equal to numResults parameter");
             for (int j = 0; j < i; j++) {
@@ -261,7 +261,7 @@ public class ListEntitiesTest extends BaseTestClass {
         throws AuthenticationException, IOException, URISyntaxException, InterruptedException
{
 
         EntityElement[] entities = helper.listEntities(
-            "numResults=2&fields=status,tags&filterBy=STATUS:SUBMITTED&orderBy=name&tags="
+ tags[2],
+            null, "numResults=2&fields=status,tags&filterBy=STATUS:SUBMITTED&orderBy=name&tags="
+ tags[2],
             null).getEntityList().getElements();
         if (entities != null) {
             SoftAssert softAssert = new SoftAssert();


Mime
View raw message