You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tez.apache.org by sr...@apache.org on 2016/01/29 20:55:52 UTC

tez git commit: TEZ-3061. Tez UI 2: Display in-progress vertex table in DAG details (sree)

Repository: tez
Updated Branches:
  refs/heads/TEZ-2980 6f366403d -> 61965f194


TEZ-3061. Tez UI 2: Display in-progress vertex table in DAG details (sree)


Project: http://git-wip-us.apache.org/repos/asf/tez/repo
Commit: http://git-wip-us.apache.org/repos/asf/tez/commit/61965f19
Tree: http://git-wip-us.apache.org/repos/asf/tez/tree/61965f19
Diff: http://git-wip-us.apache.org/repos/asf/tez/diff/61965f19

Branch: refs/heads/TEZ-2980
Commit: 61965f194cfe84e3a67c9221b9539081f0bd0999
Parents: 6f36640
Author: Sreenath Somarajapuram <sr...@apache.org>
Authored: Sat Jan 30 01:25:20 2016 +0530
Committer: Sreenath Somarajapuram <sr...@apache.org>
Committed: Sat Jan 30 01:25:20 2016 +0530

----------------------------------------------------------------------
 TEZ-2980-CHANGES.txt                            |  1 +
 tez-ui2/src/main/webapp/app/controllers/dag.js  |  4 +-
 .../webapp/app/controllers/dag/index/index.js   | 79 ++++++++++++++++++++
 tez-ui2/src/main/webapp/app/models/vertex-am.js | 10 +++
 tez-ui2/src/main/webapp/app/models/vertex.js    | 53 ++++++++++---
 tez-ui2/src/main/webapp/app/router.js           |  1 +
 .../main/webapp/app/routes/dag/index/index.js   | 57 ++++++++++++++
 .../main/webapp/app/serializers/vertex-am.js    |  9 ++-
 .../src/main/webapp/app/serializers/vertex.js   | 10 +--
 .../src/main/webapp/app/services/pollster.js    | 56 +++++++++-----
 .../src/main/webapp/app/templates/dag/index.hbs |  3 +
 .../webapp/app/templates/dag/index/index.hbs    | 38 ++++++++++
 .../unit/controllers/dag/index/index-test.js    | 39 ++++++++++
 .../webapp/tests/unit/models/vertex-test.js     |  5 +-
 .../tests/unit/routes/dag/index/index-test.js   | 50 +++++++++++++
 15 files changed, 380 insertions(+), 35 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tez/blob/61965f19/TEZ-2980-CHANGES.txt
----------------------------------------------------------------------
diff --git a/TEZ-2980-CHANGES.txt b/TEZ-2980-CHANGES.txt
index 60eb715..fc99577 100644
--- a/TEZ-2980-CHANGES.txt
+++ b/TEZ-2980-CHANGES.txt
@@ -27,3 +27,4 @@ ALL CHANGES:
   TEZ-3059. Tez UI 2: Make refresh functional
   TEZ-3070. Tez UI 2: Jenkins build is failing
   TEZ-3060. Tez UI 2: Activate auto-refresh
+  TEZ-3061. Tez UI 2: Display in-progress vertex table in DAG details

http://git-wip-us.apache.org/repos/asf/tez/blob/61965f19/tez-ui2/src/main/webapp/app/controllers/dag.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/controllers/dag.js b/tez-ui2/src/main/webapp/app/controllers/dag.js
index f2b04d1..4c8ec1a 100644
--- a/tez-ui2/src/main/webapp/app/controllers/dag.js
+++ b/tez-ui2/src/main/webapp/app/controllers/dag.js
@@ -26,14 +26,14 @@ export default ParentController.extend({
 
     return [{
       text: `DAG [ ${name} ]`,
-      routeName: "dag.index",
+      routeName: "dag.index.index",
       model: this.get("model.entityID")
     }];
   }),
 
   tabs: [{
     text: "DAG Details",
-    routeName: "dag.index"
+    routeName: "dag.index.index"
   }, {
     text: "DAG Counters",
     routeName: "dag.counters"

http://git-wip-us.apache.org/repos/asf/tez/blob/61965f19/tez-ui2/src/main/webapp/app/controllers/dag/index/index.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/controllers/dag/index/index.js b/tez-ui2/src/main/webapp/app/controllers/dag/index/index.js
new file mode 100644
index 0000000..8dc27a5
--- /dev/null
+++ b/tez-ui2/src/main/webapp/app/controllers/dag/index/index.js
@@ -0,0 +1,79 @@
+/**
+ * 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.
+ */
+
+import MultiTableController from '../../multi-table';
+import ColumnDefinition from 'em-table/utils/column-definition';
+
+export default MultiTableController.extend({
+  columns: ColumnDefinition.make([{
+    id: 'name',
+    headerTitle: 'Vertex Name',
+    contentPath: 'name',
+    cellComponentName: 'em-table-linked-cell',
+    getCellContent: function (row) {
+      return {
+        routeName: "vertex",
+        model: row.get("entityID"),
+        text: row.get("name")
+      };
+    }
+  },{
+    id: 'status',
+    headerTitle: 'Status',
+    contentPath: 'status',
+    cellComponentName: 'em-table-status-cell',
+    observePath: true
+  },{
+    id: 'progress',
+    headerTitle: 'Progress',
+    contentPath: 'progress',
+    cellComponentName: 'em-table-progress-cell',
+    observePath: true
+  },{
+    id: 'totalTasks',
+    headerTitle: 'Total Tasks',
+    contentPath: 'totalTasks',
+    observePath: true
+  },{
+    id: 'successfulTasks',
+    headerTitle: 'Successful Tasks',
+    contentPath: 'successfulTasks',
+    observePath: true
+  },{
+    id: 'runningTasks',
+    headerTitle: 'Running Tasks',
+    contentPath: 'runningTasks',
+    observePath: true
+  },{
+    id: 'pendingTasks',
+    headerTitle: 'Pending Tasks',
+    contentPath: 'pendingTasks',
+    observePath: true
+  },{
+    id: 'failedTaskAttempts',
+    headerTitle: 'Failed Task Attempts',
+    contentPath: 'failedTaskAttempts',
+    observePath: true
+  },{
+    id: 'killedTaskAttempts',
+    headerTitle: 'Killed Task Attempts',
+    contentPath: 'killedTaskAttempts',
+    observePath: true
+  }])
+
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/61965f19/tez-ui2/src/main/webapp/app/models/vertex-am.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/models/vertex-am.js b/tez-ui2/src/main/webapp/app/models/vertex-am.js
index 55722b6..54339b2 100644
--- a/tez-ui2/src/main/webapp/app/models/vertex-am.js
+++ b/tez-ui2/src/main/webapp/app/models/vertex-am.js
@@ -16,7 +16,17 @@
  * limitations under the License.
  */
 
+import DS from 'ember-data';
+
 import AMModel from './am';
 
 export default AMModel.extend({
+
+  totalTasks: DS.attr("number"),
+  successfulTasks: DS.attr("number"),
+  runningTasks: DS.attr("number"),
+  pendingTasks: DS.attr("number"),
+  failedTaskAttempts: DS.attr("number"),
+  killedTaskAttempts: DS.attr("number"),
+
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/61965f19/tez-ui2/src/main/webapp/app/models/vertex.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/models/vertex.js b/tez-ui2/src/main/webapp/app/models/vertex.js
index 51e50b1..d8f1b43 100644
--- a/tez-ui2/src/main/webapp/app/models/vertex.js
+++ b/tez-ui2/src/main/webapp/app/models/vertex.js
@@ -21,6 +21,16 @@ import DS from 'ember-data';
 
 import AMTimelineModel from './am-timeline';
 
+function valueComputerFactory(path1, path2) {
+  return function () {
+    var value = this.get(path1);
+    if(value === undefined || value === null){
+      value = this.get(path2);
+    }
+    return value;
+  };
+}
+
 export default AMTimelineModel.extend({
   needs: {
     dag: {
@@ -53,19 +63,44 @@ export default AMTimelineModel.extend({
   lastTaskFinishTime: DS.attr('number'),
 
   totalTasks: DS.attr('number'),
-  failedTasks: DS.attr('number'),
-  successfulTasks: DS.attr('number'),
-  killedTasks: DS.attr('number'),
+  _failedTasks: DS.attr('number'),
+  _successfulTasks: DS.attr('number'),
+  _killedTasks: DS.attr('number'),
+  failedTasks: Ember.computed("am.failedTasks", "_failedTasks",
+    valueComputerFactory("am.failedTasks", "_failedTasks")
+  ),
+  successfulTasks: Ember.computed("am.successfulTasks", "_successfulTasks",
+    valueComputerFactory("am.successfulTasks", "_successfulTasks")
+  ),
+  killedTasks: Ember.computed("am.killedTasks", "_killedTasks",
+    valueComputerFactory("am.killedTasks", "_killedTasks")
+  ),
 
-  runningTasks: Ember.computed("status", function () {
-    return this.get("status") === 'SUCCEEDED' ? 0 : null;
+  runningTasks: Ember.computed("am.runningTasks", "status", function () {
+    var runningTasks = this.get("am.runningTasks");
+    if(runningTasks === undefined) {
+      runningTasks = this.get("status") === 'SUCCEEDED' ? 0 : null;
+    }
+    return  runningTasks;
   }),
-  pendingTasks: Ember.computed("status", function () {
-    return this.get("status") === 'SUCCEEDED' ? 0 : null;
+  pendingTasks: Ember.computed("totalTasks", "successfulTasks", "runningTasks", function () {
+    var pendingTasks = null,
+        runningTasks = this.get("runningTasks"),
+        totalTasks = this.get("totalTasks");
+    if(totalTasks!== null && runningTasks !== null) {
+      pendingTasks = totalTasks - this.get("successfulTasks") - runningTasks;
+    }
+    return pendingTasks;
   }),
 
-  failedTaskAttempts: DS.attr('number'),
-  killedTaskAttempts: DS.attr('number'),
+  _failedTaskAttempts: DS.attr('number'),
+  _killedTaskAttempts: DS.attr('number'),
+  failedTaskAttempts: Ember.computed("am.failedTaskAttempts", "_failedTaskAttempts",
+    valueComputerFactory("am.failedTaskAttempts", "_failedTaskAttempts")
+  ),
+  killedTaskAttempts: Ember.computed("am.killedTaskAttempts", "_killedTaskAttempts",
+    valueComputerFactory("am.killedTaskAttempts", "_killedTaskAttempts")
+  ),
 
   minDuration: DS.attr('number'),
   maxDuration: DS.attr('number'),

http://git-wip-us.apache.org/repos/asf/tez/blob/61965f19/tez-ui2/src/main/webapp/app/router.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/router.js b/tez-ui2/src/main/webapp/app/router.js
index db26954..1f54580 100644
--- a/tez-ui2/src/main/webapp/app/router.js
+++ b/tez-ui2/src/main/webapp/app/router.js
@@ -30,6 +30,7 @@ Router.map(function() {
     this.route('tasks');
     this.route('attempts');
     this.route('counters');
+    this.route('index', function() {});
   });
   this.route('vertex', {path: '/vertex/:vertex_id'}, function() {
     this.route('tasks');

http://git-wip-us.apache.org/repos/asf/tez/blob/61965f19/tez-ui2/src/main/webapp/app/routes/dag/index/index.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/routes/dag/index/index.js b/tez-ui2/src/main/webapp/app/routes/dag/index/index.js
new file mode 100644
index 0000000..15d485f
--- /dev/null
+++ b/tez-ui2/src/main/webapp/app/routes/dag/index/index.js
@@ -0,0 +1,57 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import MultiAmPollsterRoute from '../../multi-am-pollster';
+
+export default MultiAmPollsterRoute.extend({
+  title: "DAG Details",
+
+  loaderNamespace: "dag",
+
+  setupController: function (controller, model) {
+    this._super(controller, model);
+    Ember.run.later(this, "startCrumbBubble");
+  },
+
+  load: function (value, query, options) {
+    return this.get("loader").query('vertex', {
+      dagID: this.modelFor("dag").get("id")
+    }, options);
+  },
+
+  _canPollObserver: Ember.observer("canPoll", function () {
+    if(this.get("canPoll")) {
+      this.get("polling").setPoll(this.pollData, this, "dag.index.index");
+    }
+    else {
+      this.get("polling").resetPoll("dag.index.index");
+    }
+  }),
+
+  actions: {
+    reload: function () {
+      return true;
+    },
+    willTransition: function () {
+      this.set("loadedValue", null);
+      return true;
+    },
+  }
+
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/61965f19/tez-ui2/src/main/webapp/app/serializers/vertex-am.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/serializers/vertex-am.js b/tez-ui2/src/main/webapp/app/serializers/vertex-am.js
index 163cf5c..e3b7eaf 100644
--- a/tez-ui2/src/main/webapp/app/serializers/vertex-am.js
+++ b/tez-ui2/src/main/webapp/app/serializers/vertex-am.js
@@ -19,5 +19,12 @@
 import AMSerializer from './am';
 
 export default AMSerializer.extend({
-  payloadNamespace: "vertices"
+  payloadNamespace: "vertices",
+
+  maps: {
+    successfulTasks: "succeededTasks",
+    runningTasks: "runningTasks",
+    failedTaskAttempts: "failedTaskAttempts",
+    killedTaskAttempts: "killedTaskAttempts",
+  }
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/61965f19/tez-ui2/src/main/webapp/app/serializers/vertex.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/serializers/vertex.js b/tez-ui2/src/main/webapp/app/serializers/vertex.js
index 7c36074..1f99f3a 100644
--- a/tez-ui2/src/main/webapp/app/serializers/vertex.js
+++ b/tez-ui2/src/main/webapp/app/serializers/vertex.js
@@ -33,12 +33,12 @@ export default TimelineSerializer.extend({
     lastTaskFinishTime: 'otherinfo.stats.lastTaskFinishTime',
 
     totalTasks: 'otherinfo.numTasks',
-    failedTasks: 'otherinfo.numFailedTasks',
-    successfulTasks: 'otherinfo.numSucceededTasks',
-    killedTasks: 'otherinfo.numKilledTasks',
+    _failedTasks: 'otherinfo.numFailedTasks',
+    _successfulTasks: 'otherinfo.numSucceededTasks',
+    _killedTasks: 'otherinfo.numKilledTasks',
 
-    failedTaskAttempts: 'otherinfo.numFailedTaskAttempts',
-    killedTaskAttempts: 'otherinfo.numKilledTaskAttempts',
+    _failedTaskAttempts: 'otherinfo.numFailedTaskAttempts',
+    _killedTaskAttempts: 'otherinfo.numKilledTaskAttempts',
 
     minDuration:  'otherinfo.stats.minTaskDuration',
     maxDuration:  'otherinfo.stats.maxTaskDuration',

http://git-wip-us.apache.org/repos/asf/tez/blob/61965f19/tez-ui2/src/main/webapp/app/services/pollster.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/services/pollster.js b/tez-ui2/src/main/webapp/app/services/pollster.js
index 8e76bc9..00a0c6c 100644
--- a/tez-ui2/src/main/webapp/app/services/pollster.js
+++ b/tez-ui2/src/main/webapp/app/services/pollster.js
@@ -1,3 +1,4 @@
+/*global more*/
 /**
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -18,7 +19,10 @@
 
 import Ember from 'ember';
 
-const STATE_STORAGE_KEY = "pollingIsActive";
+const STATE_STORAGE_KEY = "pollingIsActive",
+      DEFAULT_LABEL = "default_label";
+
+var MoreObject = more.Object;
 
 export default Ember.Service.extend({
   localStorage: Ember.inject.service("localStorage"),
@@ -30,8 +34,8 @@ export default Ember.Service.extend({
   isPolling: false,
   scheduleID: null,
 
-  poll: null,
-  pollContext: null,
+  polls: {},
+  pollCount: 0,
 
   initState: Ember.on("init", function () {
     Ember.run.later(this, function () {
@@ -43,16 +47,23 @@ export default Ember.Service.extend({
     this.callPoll();
   }),
 
-  isReady: Ember.computed("active", "poll", function () {
-    return this.get("active") && this.get("poll");
+  isReady: Ember.computed("active", "pollCount", function () {
+    return !!(this.get("active") && this.get("pollCount"));
   }),
 
   callPoll: function () {
     var that = this;
     this.unSchedulePoll();
     if(this.get("isReady") && !this.get("isPolling")) {
+      var pollsPromises = [];
+
       this.set("isPolling", true);
-      this.get("poll").call(this.get("pollContext")).finally(function () {
+
+      MoreObject.forEach(this.get("polls"), function (label, pollDef) {
+        pollsPromises.push(pollDef.callback.call(pollDef.context));
+      });
+
+      Ember.RSVP.allSettled(pollsPromises).finally(function () {
         that.set("isPolling", false);
         that.schedulePoll();
       });
@@ -66,18 +77,29 @@ export default Ember.Service.extend({
     clearTimeout(this.get("scheduleID"));
   },
 
-  setPoll: function (pollFunction, context) {
-    this.setProperties({
-      pollContext: context,
-      poll: pollFunction,
-    });
+  setPoll: function (pollFunction, context, label) {
+    var polls = this.get("polls"),
+        pollCount;
+
+    label = label || DEFAULT_LABEL;
+    polls[label] = {
+      context: context,
+      callback: pollFunction,
+    };
+    this.set("pollCount", pollCount = Object.keys(polls).length);
+
     this.callPoll();
   },
-  resetPoll: function () {
-    this.unSchedulePoll();
-    this.setProperties({
-      poll: null,
-      pollContext: null
-    });
+  resetPoll: function (label) {
+    var polls = this.get("polls"),
+        pollCount;
+
+    label = label || DEFAULT_LABEL;
+    delete polls[label];
+    this.set("pollCount", pollCount = Object.keys(polls).length);
+
+    if(!pollCount) {
+      this.unSchedulePoll();
+    }
   }
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/61965f19/tez-ui2/src/main/webapp/app/templates/dag/index.hbs
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/templates/dag/index.hbs b/tez-ui2/src/main/webapp/app/templates/dag/index.hbs
index b81194f..d7e23fc 100644
--- a/tez-ui2/src/main/webapp/app/templates/dag/index.hbs
+++ b/tez-ui2/src/main/webapp/app/templates/dag/index.hbs
@@ -73,6 +73,9 @@
       </tr>
     </tbody>
   </table>
+
+  {{outlet}}
+
 {{else}}
   {{partial "loading"}}
 {{/if}}

http://git-wip-us.apache.org/repos/asf/tez/blob/61965f19/tez-ui2/src/main/webapp/app/templates/dag/index/index.hbs
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/templates/dag/index/index.hbs b/tez-ui2/src/main/webapp/app/templates/dag/index/index.hbs
new file mode 100644
index 0000000..1321b9f
--- /dev/null
+++ b/tez-ui2/src/main/webapp/app/templates/dag/index/index.hbs
@@ -0,0 +1,38 @@
+{{!
+ * 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.
+}}
+
+{{#if loaded}}
+  {{em-table
+  columns=visibleColumns
+  rows=model
+
+  definition=definition
+
+  enableSearch=false
+  enablePagination=false
+
+  searchAction="searchChanged"
+  sortAction="sortChanged"
+  rowAction="rowCountChanged"
+  pageAction="pageChanged"
+
+  rowsChanged="rowsChanged"
+  }}
+{{else}}
+  {{partial "loading"}}
+{{/if}}

http://git-wip-us.apache.org/repos/asf/tez/blob/61965f19/tez-ui2/src/main/webapp/tests/unit/controllers/dag/index/index-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/unit/controllers/dag/index/index-test.js b/tez-ui2/src/main/webapp/tests/unit/controllers/dag/index/index-test.js
new file mode 100644
index 0000000..956eee3
--- /dev/null
+++ b/tez-ui2/src/main/webapp/tests/unit/controllers/dag/index/index-test.js
@@ -0,0 +1,39 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:dag/index/index', 'Unit | Controller | dag/index/index', {
+  // Specify the other units that are required for this test.
+  // needs: ['controller:foo']
+});
+
+test('Basic creation test', function(assert) {
+  let controller = this.subject({
+    send: Ember.K,
+    initVisibleColumns: Ember.K,
+    getCounterColumns: function () {
+      return [];
+    }
+  });
+
+  assert.ok(controller);
+  assert.ok(controller.columns);
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/61965f19/tez-ui2/src/main/webapp/tests/unit/models/vertex-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/unit/models/vertex-test.js b/tez-ui2/src/main/webapp/tests/unit/models/vertex-test.js
index c0d2047..9ca049a 100644
--- a/tez-ui2/src/main/webapp/tests/unit/models/vertex-test.js
+++ b/tez-ui2/src/main/webapp/tests/unit/models/vertex-test.js
@@ -51,8 +51,11 @@ test('pendingTasks test', function(assert) {
   let model = this.subject();
 
   Ember.run(function () {
+    model.set("totalTasks", null);
     assert.equal(model.get("pendingTasks"), null);
+    model.set("totalTasks", 2);
+    model.set("_successfulTasks", 1);
     model.set("status", "SUCCEEDED");
-    assert.equal(model.get("pendingTasks"), 0);
+    assert.equal(model.get("pendingTasks"), 1);
   });
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/61965f19/tez-ui2/src/main/webapp/tests/unit/routes/dag/index/index-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/unit/routes/dag/index/index-test.js b/tez-ui2/src/main/webapp/tests/unit/routes/dag/index/index-test.js
new file mode 100644
index 0000000..dc767ba
--- /dev/null
+++ b/tez-ui2/src/main/webapp/tests/unit/routes/dag/index/index-test.js
@@ -0,0 +1,50 @@
+/**
+ * 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.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('route:dag/index/index', 'Unit | Route | dag/index/index', {
+  // Specify the other units that are required for this test.
+  // needs: ['controller:foo']
+});
+
+test('Basic creation test', function(assert) {
+  let route = this.subject();
+
+  assert.ok(route);
+  assert.ok(route.title);
+  assert.ok(route.loaderNamespace);
+  assert.ok(route.setupController);
+  assert.ok(route.load);
+
+  assert.ok(route._canPollObserver);
+  assert.ok(route.actions.reload);
+  assert.ok(route.actions.willTransition);
+});
+
+test('setupController test', function(assert) {
+  assert.expect(1);
+
+  let route = this.subject({
+    startCrumbBubble: function () {
+      assert.ok(true);
+    }
+  });
+
+  route.setupController({}, {});
+});