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/03/18 11:05:03 UTC

tez git commit: TEZ-3170. Tez UI 2: Swimlane - Display computed events, event bars & dependencies (sree)

Repository: tez
Updated Branches:
  refs/heads/master 44c660a34 -> c9c7d8099


TEZ-3170. Tez UI 2: Swimlane - Display computed events, event bars & dependencies (sree)


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

Branch: refs/heads/master
Commit: c9c7d80997373f424abba9a0ec6a99b8db983d2b
Parents: 44c660a
Author: Sreenath Somarajapuram <sr...@apache.org>
Authored: Fri Mar 18 15:34:33 2016 +0530
Committer: Sreenath Somarajapuram <sr...@apache.org>
Committed: Fri Mar 18 15:34:33 2016 +0530

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../components/em-swimlane-blocking-event.js    |  50 +++++++
 .../app/components/em-swimlane-event-bar.js     |  67 +++++++++
 .../webapp/app/components/em-swimlane-event.js  |  23 ++-
 .../components/em-swimlane-process-visual.js    |  11 +-
 .../main/webapp/app/components/em-swimlane.js   |  79 ++++++++++
 .../main/webapp/app/controllers/dag/swimlane.js |  35 +++++
 .../src/main/webapp/app/styles/em-swimlane.less |  58 +++++---
 .../components/em-swimlane-blocking-event.hbs   |  19 +++
 .../components/em-swimlane-event-bar.hbs        |  19 +++
 .../templates/components/em-swimlane-event.hbs  |   4 +-
 .../components/em-swimlane-process-visual.hbs   |  21 ++-
 .../app/templates/components/em-swimlane.hbs    |   5 +-
 .../webapp/app/templates/dag/index/index.hbs    |   1 +
 .../main/webapp/app/templates/dag/swimlane.hbs  |   3 +-
 tez-ui2/src/main/webapp/app/utils/process.js    | 101 +++++++++++++
 .../src/main/webapp/app/utils/vertex-process.js | 109 ++++++++++++++
 .../em-swimlane-blocking-event-test.js          |  78 ++++++++++
 .../components/em-swimlane-event-bar-test.js    |  43 ++++++
 .../components/em-swimlane-event-test.js        |  13 +-
 .../em-swimlane-process-visual-test.js          |  15 +-
 .../integration/components/em-swimlane-test.js  |  42 +++++-
 .../tests/unit/controllers/dag/swimlane-test.js |  45 ++++++
 .../webapp/tests/unit/utils/process-test.js     | 147 +++++++++++++++++++
 .../tests/unit/utils/vertex-process-test.js     |  98 +++++++++++++
 25 files changed, 1034 insertions(+), 53 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index f14a2fa..30e4fc6 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1885,6 +1885,7 @@ ALL CHANGES
   TEZ-642. Fix poms for release. (hitesh)
   TEZ-643. Change getProgress APIs to return some form of progress and 1.0f once the map or reduce phase complete. (sseth)
   TEZ-3160. Tez UI 2: Swimlane - Create swimlane page & component (sree)
+  TEZ-3170. Tez UI 2: Swimlane - Display computed events, event bars & dependencies (sree)
 
 Release 0.2.0-incubating: 2013-11-30
 

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/app/components/em-swimlane-blocking-event.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/components/em-swimlane-blocking-event.js b/tez-ui2/src/main/webapp/app/components/em-swimlane-blocking-event.js
new file mode 100644
index 0000000..a58ded8
--- /dev/null
+++ b/tez-ui2/src/main/webapp/app/components/em-swimlane-blocking-event.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 Ember from 'ember';
+
+export default Ember.Component.extend({
+
+  process: null,
+  blocking: null,
+  events: null,
+
+  classNames: ["em-swimlane-blocking-event"],
+
+  blockingEvent: Ember.computed("events.length", "process.blockingEventName", function () {
+    var events = this.get("events"),
+        blockingEventName = this.get("process.blockingEventName");
+
+    return events.find(function (event) {
+      return event.name === blockingEventName;
+    });
+  }),
+
+  didInsertElement: Ember.observer("blockingEvent", function () {
+    var blockerEventHeight = (this.get("blocking.index") - this.get("process.index")) * 30;
+
+    this.$().css({
+      "left": this.get("blockingEvent.pos") + "%"
+    });
+    this.$(".event-line").css({
+      "height": `${blockerEventHeight}px`,
+      "border-color": this.get("process").getColor()
+    });
+  }),
+
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/app/components/em-swimlane-event-bar.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/components/em-swimlane-event-bar.js b/tez-ui2/src/main/webapp/app/components/em-swimlane-event-bar.js
new file mode 100644
index 0000000..e0e835e
--- /dev/null
+++ b/tez-ui2/src/main/webapp/app/components/em-swimlane-event-bar.js
@@ -0,0 +1,67 @@
+/**
+ * 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';
+
+export default Ember.Component.extend({
+
+  process: null,
+  events: [],
+
+  bars: [],
+  bar: null,
+  barIndex: 0,
+
+  classNames: ["em-swimlane-event-bar"],
+
+  fromEvent: Ember.computed("events.length", "bar.fromEvent", function () {
+    var events = this.get("events"),
+        fromEventName = this.get("bar.fromEvent");
+    return events.find(function (event) {
+      return event.name === fromEventName;
+    });
+  }),
+  toEvent: Ember.computed("events.length", "bar.toEvent", function () {
+    var events = this.get("events"),
+        toEventName = this.get("bar.toEvent");
+    return events.find(function (event) {
+      return event.name === toEventName;
+    });
+  }),
+
+  didInsertElement: Ember.observer("fromEvent.pos", "toEvent.pos", "barIndex", function () {
+    var fromEventPos = this.get("fromEvent.pos"),
+        toEventPos = this.get("toEvent.pos"),
+        color = this.get("bar.color") ||
+            this.get("process").getColor(1 - (this.get("barIndex") / this.get("bars.length")));
+
+    if(fromEventPos && toEventPos) {
+      this.$().show();
+      this.$(".event-bar").css({
+        left: fromEventPos + "%",
+        right: (100 - toEventPos) + "%",
+        "background-color": color,
+        "border-color": this.get("process").getColor()
+      });
+    }
+    else {
+      this.$().hide();
+    }
+  })
+
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/app/components/em-swimlane-event.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/components/em-swimlane-event.js b/tez-ui2/src/main/webapp/app/components/em-swimlane-event.js
index a292085..5eb6f0a 100644
--- a/tez-ui2/src/main/webapp/app/components/em-swimlane-event.js
+++ b/tez-ui2/src/main/webapp/app/components/em-swimlane-event.js
@@ -20,14 +20,25 @@ import Ember from 'ember';
 
 export default Ember.Component.extend({
 
-  title: null,
-  position: 0,
-  time: 0,
+  process: null,
+  event: null,
 
   classNames: ["em-swimlane-event"],
 
-  didInsertElement: Ember.observer("position", function () {
-    this.$().css("left", this.get("position") + "%");
-  })
+  didInsertElement: Ember.observer("event.pos", function () {
+    var color = this.get("process").getColor();
+
+    this.$().css({
+      "left": this.get("event.pos") + "%"
+    });
+    this.$(".event-line").css("border-color", color);
+    this.$(".event-bubble").css("border-color", color);
+  }),
+
+  actions: {
+    showTooltip: function () {
+      console.log(this.get("event.name"));
+    }
+  }
 
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/app/components/em-swimlane-process-visual.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/components/em-swimlane-process-visual.js b/tez-ui2/src/main/webapp/app/components/em-swimlane-process-visual.js
index b21eadb..5942cf1 100644
--- a/tez-ui2/src/main/webapp/app/components/em-swimlane-process-visual.js
+++ b/tez-ui2/src/main/webapp/app/components/em-swimlane-process-visual.js
@@ -26,17 +26,18 @@ export default Ember.Component.extend({
   endTime: 0,
   timeWindow: 0,
 
-  events: [],
+  normalizedEvents: [],
   startEvent: null,
   endEvent: null,
 
+  eventBars: [],
   classNames: ["em-swimlane-process-visual"],
 
   didInsertElement: function () {
     Ember.run.later(this, "normalizeEvents");
   },
 
-  normalizeEvents: Ember.observer("process.events.@each.timestamp", "startTime", "timeWindow", function () {
+  normalizeEvents: Ember.observer("process.events.@each.time", "startTime", "timeWindow", function () {
     var events = Ember.get(this.get("process"), "events") || [],
         startEvent,
         endEvent,
@@ -45,8 +46,7 @@ export default Ember.Component.extend({
         timeWindow = this.get("timeWindow");
 
     events = events.map(function (event) {
-      var position = ((event.timestamp - startTime) / timeWindow) * 100;
-
+      var position = ((event.time - startTime) / timeWindow) * 100;
       event = {
         name: event.name,
         text: event.text || event.name,
@@ -65,7 +65,7 @@ export default Ember.Component.extend({
     });
 
     this.setProperties({
-      events: events,
+      normalizedEvents: events,
       startEvent: startEvent,
       endEvent: endEvent
     });
@@ -75,6 +75,7 @@ export default Ember.Component.extend({
     this.$(".event-window-line").css({
       left: this.get("startEvent.pos") + "%",
       right: (100 - this.get("endEvent.pos")) + "%",
+      "background-color": this.get("process").getColor()
     });
   })
 

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/app/components/em-swimlane.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/components/em-swimlane.js b/tez-ui2/src/main/webapp/app/components/em-swimlane.js
index 93a2cff..3f22c50 100644
--- a/tez-ui2/src/main/webapp/app/components/em-swimlane.js
+++ b/tez-ui2/src/main/webapp/app/components/em-swimlane.js
@@ -19,6 +19,18 @@
 import Ember from 'ember';
 
 import ProcessDefinition from '../utils/process-definition';
+import Process from '../utils/process';
+
+function getVibrantHSL(colorNum, totalColors) {
+  if (totalColors < 1){
+    totalColors = 1;
+  }
+  return {
+    h: colorNum * (360 / totalColors) % 360,
+    s: 100 - (colorNum % 2) * 30,
+    l: 40
+  };
+}
 
 export default Ember.Component.extend({
 
@@ -30,8 +42,75 @@ export default Ember.Component.extend({
   startTime: null,
   endTime: null,
 
+  eventBars: [],
+
   timeWindow: Ember.computed("startTime", "endTime", function () {
     return Math.max(0, this.get("endTime") - this.get("startTime"));
+  }),
+
+  normalizedProcesses: Ember.computed("processes.@each.blockers", function () {
+    var processes = this.get("processes"),
+        processCount = processes.length,
+        normalizedProcesses,
+        idHash = {},
+        containsBlockers = false;
+
+    // Validate and reset blocking
+    processes.forEach(function (process) {
+      if(!(process instanceof Process)) {
+        Ember.Logger.error("em-swimlane : Unknown type, must be of type Process");
+      }
+
+      if(process.get("blockers.length")) {
+        containsBlockers = true;
+      }
+      process.set("blocking", Ember.A());
+    });
+
+    if(containsBlockers) {
+      normalizedProcesses = [];
+
+      // Recreate blocking list
+      processes.forEach(function (process) {
+        var blockers = process.get("blockers");
+        if(blockers) {
+          blockers.forEach(function (blocker) {
+            blocker.get("blocking").push(process);
+          });
+        }
+      });
+
+      // Give an array of the processes in blocking order
+      processes.forEach(function (process) {
+        if(process.get("blocking.length") === 0) { // The root processes
+          normalizedProcesses.push(process);
+          normalizedProcesses.push.apply(normalizedProcesses, process.getAllBlockers());
+        }
+      });
+      normalizedProcesses.reverse();
+      normalizedProcesses = normalizedProcesses.filter(function (process, index) {
+        // Filters out the recurring processes in the list (after graph traversal), we just
+        // need the top processes
+        var id = process.get("_id");
+        if(idHash[id] === undefined) {
+          idHash[id] = index;
+        }
+        return idHash[id] === index;
+      });
+    }
+    else {
+      normalizedProcesses = processes;
+    }
+
+    // Set process colors & index
+    normalizedProcesses.forEach(function (process, index) {
+      process.setProperties({
+        color: getVibrantHSL(index, processCount),
+        index: index
+      });
+    });
+
+    return Ember.A(normalizedProcesses);
   })
 
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/app/controllers/dag/swimlane.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/controllers/dag/swimlane.js b/tez-ui2/src/main/webapp/app/controllers/dag/swimlane.js
index 1028a2c..1699708 100644
--- a/tez-ui2/src/main/webapp/app/controllers/dag/swimlane.js
+++ b/tez-ui2/src/main/webapp/app/controllers/dag/swimlane.js
@@ -20,6 +20,7 @@ import Ember from 'ember';
 
 import MultiTableController from '../multi-table';
 import ColumnDefinition from 'em-table/utils/column-definition';
+import VertexProcess from '../../utils/vertex-process';
 
 import fullscreen from 'em-tgraph/utils/fullscreen';
 
@@ -40,4 +41,38 @@ export default MultiTableController.extend({
     }
   },
 
+  processes: Ember.computed("model", function () {
+    var processes = [],
+        processHash = {},
+
+        dagPlanEdges = this.get("model.firstObject.dag.edges");
+
+    // Create process instances for each vertices
+    this.get("model").forEach(function (vertex) {
+      var process = VertexProcess.create({
+        vertex: vertex,
+        blockers: Ember.A()
+      });
+      processHash[vertex.get("name")] = process;
+      processes.push(process);
+    });
+
+    // Add process(vertex) dependencies based on dagPlan
+    dagPlanEdges.forEach(function (edge) {
+      var process = processHash[edge.outputVertexName];
+      if(process) {
+        process.blockers.push(processHash[edge.inputVertexName]);
+      }
+    });
+
+    return Ember.A(processes);
+  }),
+
+  eventBars: [{
+    fromEvent: "VERTEX_TASK_START",
+    toEvent: "VERTEX_TASK_FINISH",
+  }, {
+    fromEvent: "BLOCKING_VERTICES_COMPLETE",
+    toEvent: "VERTEX_TASK_FINISH",
+  }]
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/app/styles/em-swimlane.less
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/styles/em-swimlane.less b/tez-ui2/src/main/webapp/app/styles/em-swimlane.less
index 5087e8f..a982712 100644
--- a/tez-ui2/src/main/webapp/app/styles/em-swimlane.less
+++ b/tez-ui2/src/main/webapp/app/styles/em-swimlane.less
@@ -25,14 +25,13 @@
 
   .process-names {
     width: 100px;
+    border-right: 1px solid @border-color;
   }
   .process-visuals {
     position: absolute;
     left: 100px;
     right: 0px;
     top: 0px;
-
-    border-left: 1px solid @border-color;
   }
 }
 
@@ -52,20 +51,32 @@
   position: relative;
   height: 30px;
 
+  margin-left: 10px;
+
   cursor: pointer;
 
   .base-line {
     position: relative;
     height: 1px;
+    margin-left: -10px;
     top: unit(unit(@process-height) * 0.5, get-unit(@process-height));
     border-top: 1px dotted @border-color;
   }
 
   .event-window-line {
     position: absolute;
-    top: unit(unit(@process-height) * 0.5, get-unit(@process-height));
-    border-top: 1px solid;
-    height: 1px;
+    top: unit(unit(@process-height) * 0.5 - 1, get-unit(@process-height));
+    height: 3px;
+  }
+
+  .event-bar {
+    position: absolute;
+    top: unit((unit(@process-height) * 0.5) - 10, get-unit(@process-height));
+    height: 20px;
+    background-color: @border-lite;
+    border-radius: 2px;
+    border: 1px solid;
+    margin-right: -1px;
   }
 }
 
@@ -73,10 +84,10 @@
   position: absolute;
   top: unit(unit(@process-height) * 0.5, get-unit(@process-height));
 
-  .event-bar {
+  .event-line {
     position: absolute;
-    top: -10px;
-    height: 20px;
+    top: -9px;
+    height: 18px;
     border-left: 1px solid;
   }
 
@@ -87,23 +98,36 @@
     bottom: 0;
     left: 0;
 
-    -webkit-transition: top .2s, right .2s, bottom .2s, left .2s; /* Safari */
-    transition: top .2s, right .2s, bottom .2s, left .2s;
-    transition-timing-function: cubic-bezier(.87,-.41,.19,1.44);
+    -webkit-transition: top .2s, right .2s, bottom .2s, left .2s, border-width .2s; /* Safari */
+    transition: top .2s, right .2s, bottom .2s, left .2s, border-width .2s;
+    transition-timing-function: cubic-bezier(1.44);
 
-    border-radius: 5px;
+    border-radius: 7px;
+    border: 0px solid;
+    background-color: white;
+  }
+}
+
+.em-swimlane-blocking-event {
+  position: absolute;
+  top: unit(unit(@process-height) * 0.5, get-unit(@process-height));
 
-    background-color: black;
+  .event-line {
+    position: absolute;
+    top: 0px;
+    border-left: 1px solid;
   }
 }
 
 .em-swimlane-process-visual {
   &:hover {
     .event-bubble {
-      top: -5px;
-      right: -6px;
-      bottom: -6px;
-      left: -5px;
+      top: -7px;
+      right: -8px;
+      bottom: -8px;
+      left: -7px;
+
+      border: 2px solid;
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-blocking-event.hbs
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-blocking-event.hbs b/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-blocking-event.hbs
new file mode 100644
index 0000000..0fabd16
--- /dev/null
+++ b/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-blocking-event.hbs
@@ -0,0 +1,19 @@
+{{!
+ * 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.
+}}
+
+<div class="event-line"></div>

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-event-bar.hbs
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-event-bar.hbs b/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-event-bar.hbs
new file mode 100644
index 0000000..b2cc9bd
--- /dev/null
+++ b/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-event-bar.hbs
@@ -0,0 +1,19 @@
+{{!
+ * 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.
+}}
+
+<div class="event-bar"></div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-event.hbs
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-event.hbs b/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-event.hbs
index 0211466..9b6330f 100644
--- a/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-event.hbs
+++ b/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-event.hbs
@@ -16,5 +16,5 @@
  * limitations under the License.
 }}
 
-<div class="event-bar"></div>
-<div class="event-bubble"></div>
\ No newline at end of file
+<div class="event-line"></div>
+<div class="event-bubble" {{action "showTooltip" on="mouseEnter"}}></div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-process-visual.hbs
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-process-visual.hbs b/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-process-visual.hbs
index 6a3a7b9..6e4603d 100644
--- a/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-process-visual.hbs
+++ b/tez-ui2/src/main/webapp/app/templates/components/em-swimlane-process-visual.hbs
@@ -17,11 +17,18 @@
 }}
 
 <div class="base-line"></div>
-{{#each events as |event|}}
-  {{em-swimlane-event
-  title=event.text
-  position=event.pos
-  time=event.time
-  }}
+<div class="event-window-line"></div>
+{{#each process.blocking as |blocking|}}
+  {{em-swimlane-blocking-event process=process blocking=blocking events=normalizedEvents}}
+{{/each}}
+{{#each eventBars as |bar index|}}
+  {{em-swimlane-event-bar
+    events=normalizedEvents
+    bar=bar
+    barIndex=index
+    bars=eventBars
+    process=process}}
+{{/each}}
+{{#each normalizedEvents as |event|}}
+  {{em-swimlane-event process=process event=event}}
 {{/each}}
-<div class="event-window-line"></div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/app/templates/components/em-swimlane.hbs
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/templates/components/em-swimlane.hbs b/tez-ui2/src/main/webapp/app/templates/components/em-swimlane.hbs
index a9ab876..a4777f0 100644
--- a/tez-ui2/src/main/webapp/app/templates/components/em-swimlane.hbs
+++ b/tez-ui2/src/main/webapp/app/templates/components/em-swimlane.hbs
@@ -17,14 +17,15 @@
 }}
 
 <div class="process-names">
-  {{#each processes as |process|}}
+  {{#each normalizedProcesses as |process|}}
     {{em-swimlane-process-name process=process definition=processDefinition}}
   {{/each}}
 </div><div class="process-visuals">
-  {{#each processes as |process|}}
+  {{#each normalizedProcesses as |process|}}
     {{em-swimlane-process-visual
       process=process
       definition=processDefinition
+      eventBars=eventBars
       startTime=startTime
       endTime=endTime
       timeWindow=timeWindow

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/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
index 02c88da..c6864fb 100644
--- a/tez-ui2/src/main/webapp/app/templates/dag/index/index.hbs
+++ b/tez-ui2/src/main/webapp/app/templates/dag/index/index.hbs
@@ -68,6 +68,7 @@
   enableSearch=false
   enablePagination=false
 
+  rowCount=model.length
   searchAction="searchChanged"
   sortAction="sortChanged"
   rowAction="rowCountChanged"

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/app/templates/dag/swimlane.hbs
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/templates/dag/swimlane.hbs b/tez-ui2/src/main/webapp/app/templates/dag/swimlane.hbs
index adb8586..a2b2cbe 100644
--- a/tez-ui2/src/main/webapp/app/templates/dag/swimlane.hbs
+++ b/tez-ui2/src/main/webapp/app/templates/dag/swimlane.hbs
@@ -25,7 +25,8 @@
 
     {{em-swimlane
       columns=visibleColumns
-      processes=model
+      processes=processes
+      eventBars=eventBars
       startTime=model.firstObject.dag.startTime
       endTime=model.firstObject.dag.endTime
     }}

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/app/utils/process.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/utils/process.js b/tez-ui2/src/main/webapp/app/utils/process.js
new file mode 100644
index 0000000..eba3e72
--- /dev/null
+++ b/tez-ui2/src/main/webapp/app/utils/process.js
@@ -0,0 +1,101 @@
+/**
+ * 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';
+
+var processIndex = 1;
+
+export default Ember.Object.extend({
+  _id: null,
+
+  name: null,
+  events: null,
+
+  index: 0,
+  color: null,
+
+  blockers: null, // Array of processes that's blocking the current process
+  blocking: null, // Array of processes blocked by the current process
+
+  init: function () {
+    this.set("_id", `process-id-${processIndex}`);
+    processIndex++;
+  },
+
+  getColor: function (lightnessFactor) {
+    var color = this.get("color"),
+        l;
+
+    if(!color) {
+      return "#0";
+    }
+    l = color.l;
+    if(lightnessFactor !== undefined) {
+      l += 5 + 25 * lightnessFactor;
+    }
+    return `hsl( ${color.h}, ${color.s}%, ${l}% )`;
+  },
+
+  startEvent: Ember.computed("events.@each.time", function () {
+    var events = this.get("events"),
+        startEvent;
+    if(events) {
+      startEvent = events[0];
+        events.forEach(function (event) {
+          if(startEvent.time > event.time) {
+            startEvent = event;
+          }
+      });
+    }
+    return startEvent;
+  }),
+
+  endEvent: Ember.computed("events.@each.time", function () {
+    var events = this.get("events"),
+        endEvent;
+    if(events) {
+      endEvent = events[events.length - 1];
+      events.forEach(function (event) {
+        if(endEvent.time < event.time) {
+          endEvent = event;
+        }
+      });
+    }
+    return endEvent;
+  }),
+
+  getAllBlockers: function (parentHash) {
+    var blockers = [],
+        currentId = this.get("_id");
+
+    parentHash = parentHash || {}; // To keep a check on cyclic blockers
+
+    parentHash[currentId] = true;
+    if(this.get("blockers.length")) {
+      this.get("blockers").forEach(function (blocker) {
+        if(!parentHash[blocker.get("_id")]) {
+          blockers.push(blocker);
+          blockers.push.apply(blockers, blocker.getAllBlockers(parentHash));
+        }
+      });
+    }
+    parentHash[currentId] = false;
+
+    return blockers;
+  }
+
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/app/utils/vertex-process.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/app/utils/vertex-process.js b/tez-ui2/src/main/webapp/app/utils/vertex-process.js
new file mode 100644
index 0000000..e48532d
--- /dev/null
+++ b/tez-ui2/src/main/webapp/app/utils/vertex-process.js
@@ -0,0 +1,109 @@
+/**
+ * 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 Process from './process';
+
+const EVENT_TEXTS = {
+  VERTEX_INITIALIZED: "Initialized",
+  VERTEX_STARTED: "Started",
+  VERTEX_FINISHED: "Finished",
+  VERTEX_TASK_START: "First Task Start",
+  VERTEX_TASK_FINISH: "All Tasks Complete",
+  BLOCKING_VERTICES_COMPLETE: "All Blocking Vertices Complete"
+};
+
+export default Process.extend({
+  vertex: null,
+
+  name: Ember.computed.oneWay("vertex.name"),
+  completeTime: Ember.computed.oneWay("vertex.endTime"),
+
+  blockingEventName: "VERTEX_FINISHED",
+
+  events: Ember.computed(
+    "vertex.events.@each.timestamp",
+    "vertex.firstTaskStartTime",
+    "vertex.lastTaskFinishTime",
+    "unblockTime",
+    function () {
+      var events = this.get("vertex.events").map(function (event) {
+        return {
+          name: event.eventtype,
+          test: EVENT_TEXTS[event.eventtype],
+          time: event.timestamp
+        };
+      }),
+      firstTaskStartTime = this.get("vertex.firstTaskStartTime"),
+      lastTaskFinishTime = this.get("vertex.lastTaskFinishTime"),
+      unblockTime = this.get("unblockTime");
+
+      if(firstTaskStartTime) {
+        let type = "VERTEX_TASK_START";
+        events.push({
+          name: type,
+          test: EVENT_TEXTS[type],
+          time: firstTaskStartTime
+        });
+      }
+
+      if(lastTaskFinishTime) {
+        let type = "VERTEX_TASK_FINISH";
+        events.push({
+          name: type,
+          test: EVENT_TEXTS[type],
+          time: lastTaskFinishTime
+        });
+      }
+
+      if(unblockTime && unblockTime >= firstTaskStartTime) {
+        let type = "BLOCKING_VERTICES_COMPLETE";
+        events.push({
+          name: type,
+          test: EVENT_TEXTS[type],
+          time: unblockTime
+        });
+      }
+
+      return events;
+    }
+  ),
+
+  unblockTime: Ember.computed("blockers.@each.completeTime", function () {
+    var blockers = this.get("blockers"),
+        time;
+
+    if(blockers) {
+      time = 0;
+      for(var i = 0, length = blockers.length; i < length; i++) {
+        let blockerComplete = blockers[i].get("completeTime");
+
+        if(!blockerComplete) {
+          time = undefined;
+          break;
+        }
+        else if(blockerComplete > time) {
+          time = blockerComplete;
+        }
+      }
+    }
+
+    return time;
+  }),
+
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-blocking-event-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-blocking-event-test.js b/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-blocking-event-test.js
new file mode 100644
index 0000000..3ffab01
--- /dev/null
+++ b/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-blocking-event-test.js
@@ -0,0 +1,78 @@
+/**
+ * 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 { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+import Process from 'tez-ui/utils/process';
+
+moduleForComponent('em-swimlane-blocking-event', 'Integration | Component | em swimlane blocking event', {
+  integration: true
+});
+
+test('Basic creation test', function(assert) {
+  this.set("process", Process.create());
+  this.set("events", []);
+
+  this.render(hbs`{{em-swimlane-blocking-event process=process events=events}}`);
+
+  assert.equal(this.$().text().trim(), '');
+
+  // Template block usage:" + EOL +
+  this.render(hbs`
+    {{#em-swimlane-blocking-event process=process events=events}}
+      template block text
+    {{/em-swimlane-blocking-event}}
+  `);
+
+  assert.equal(this.$().text().trim(), '');
+});
+
+test('Blocking test', function(assert) {
+  var blockingEventName = "blockingEvent",
+      processIndex = 5,
+      blockingIndex = 7,
+      processColor = "#123456";
+
+  this.set("process", Process.create({
+    blockingEventName: blockingEventName,
+    index: processIndex,
+    getColor: function () {
+      return processColor;
+    },
+    events: []
+  }));
+  this.set("normalizedEvents", [{
+    name: "e1",
+    pos: 10
+  }, {
+    name: blockingEventName,
+    pos: 20
+  }, {
+    name: "e2",
+    pos: 30
+  }]);
+  this.set("blocking", Process.create({
+    index: blockingIndex
+  }));
+
+  this.render(hbs`{{em-swimlane-blocking-event process=process events=normalizedEvents blocking=blocking}}`);
+
+  assert.equal(this.$(".em-swimlane-blocking-event").attr("style").trim(), 'left: 20%;');
+  assert.equal(this.$(".event-line").css("height"), ((blockingIndex - processIndex) * 30) + "px");
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-event-bar-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-event-bar-test.js b/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-event-bar-test.js
new file mode 100644
index 0000000..d05924f
--- /dev/null
+++ b/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-event-bar-test.js
@@ -0,0 +1,43 @@
+/**
+ * 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 { moduleForComponent, test } from 'ember-qunit';
+import hbs from 'htmlbars-inline-precompile';
+
+import Process from 'tez-ui/utils/process';
+
+moduleForComponent('em-swimlane-event-bar', 'Integration | Component | em swimlane event bar', {
+  integration: true
+});
+
+test('Basic creation test', function(assert) {
+  this.set("process", Process.create());
+
+  this.render(hbs`{{em-swimlane-event-bar process=process}}`);
+
+  assert.equal(this.$().text().trim(), '');
+
+  // Template block usage:" + EOL +
+  this.render(hbs`
+    {{#em-swimlane-event-bar process=process}}
+      template block text
+    {{/em-swimlane-event-bar}}
+  `);
+
+  assert.equal(this.$().text().trim(), '');
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-event-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-event-test.js b/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-event-test.js
index 9e9764e..034d288 100644
--- a/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-event-test.js
+++ b/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-event-test.js
@@ -19,6 +19,8 @@
 import { moduleForComponent, test } from 'ember-qunit';
 import hbs from 'htmlbars-inline-precompile';
 
+import Process from 'tez-ui/utils/process';
+
 import wait from 'ember-test-helpers/wait';
 
 moduleForComponent('em-swimlane-event', 'Integration | Component | em swimlane event', {
@@ -26,15 +28,16 @@ moduleForComponent('em-swimlane-event', 'Integration | Component | em swimlane e
 });
 
 test('Basic creation test', function(assert) {
+  this.set("process", Process.create({}));
 
-  this.render(hbs`{{em-swimlane-event}}`);
+  this.render(hbs`{{em-swimlane-event process=process}}`);
 
   assert.ok(this.$(".event-bar"));
   assert.ok(this.$(".event-window"));
 
   // Template block usage:" + EOL +
   this.render(hbs`
-    {{#em-swimlane-event}}
+    {{#em-swimlane-event process=process}}
       template block text
     {{/em-swimlane-event}}
   `);
@@ -44,8 +47,12 @@ test('Basic creation test', function(assert) {
 });
 
 test('Event position test', function(assert) {
+  this.set("process", Process.create());
+  this.set("event", {
+    pos: 60
+  });
 
-  this.render(hbs`{{em-swimlane-event position=60}}`);
+  this.render(hbs`{{em-swimlane-event process=process event=event}}`);
 
   return wait().then(() => {
     assert.equal(this.$(".em-swimlane-event").attr("style").trim(), "left: 60%;", "em-swimlane-event");

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-process-visual-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-process-visual-test.js b/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-process-visual-test.js
index 5a180bb..ffc7b56 100644
--- a/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-process-visual-test.js
+++ b/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-process-visual-test.js
@@ -21,14 +21,14 @@ import hbs from 'htmlbars-inline-precompile';
 
 import wait from 'ember-test-helpers/wait';
 
+import Process from 'tez-ui/utils/process';
+
 moduleForComponent('em-swimlane-process-visual', 'Integration | Component | em swimlane process visual', {
   integration: true
 });
 
 test('Basic creation test', function(assert) {
-  this.set("process", {
-    events: []
-  });
+  this.set("process", Process.create());
 
   this.render(hbs`{{em-swimlane-process-visual process=process}}`);
 
@@ -47,19 +47,20 @@ test('Basic creation test', function(assert) {
 });
 
 test('Events test', function(assert) {
-  this.set("process", {
+  this.set("process", Process.create({
     events: [{
-      timestamp: 5
+      time: 5
     }, {
-      timestamp: 7
+      time: 7
     }]
-  });
+  }));
 
   this.render(hbs`{{em-swimlane-process-visual process=process startTime=0 timeWindow=10}}`);
 
   return wait().then(() => {
     var events = this.$(".em-swimlane-event");
 
+
     assert.equal(events.length, 2);
     assert.equal(events.eq(0).attr("style").trim(), "left: 50%;", "em-swimlane-event 1 left");
     assert.equal(events.eq(1).attr("style").trim(), "left: 70%;", "em-swimlane-event 2 left");

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-test.js b/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-test.js
index 1e4c938..59e543d 100644
--- a/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-test.js
+++ b/tez-ui2/src/main/webapp/tests/integration/components/em-swimlane-test.js
@@ -19,6 +19,8 @@
 import { moduleForComponent, test } from 'ember-qunit';
 import hbs from 'htmlbars-inline-precompile';
 
+import Process from 'tez-ui/utils/process';
+
 moduleForComponent('em-swimlane', 'Integration | Component | em swimlane', {
   integration: true
 });
@@ -27,11 +29,11 @@ test('Basic creation test', function(assert) {
   var testName1 = "TestName1",
       testName2 = "TestName2";
 
-  this.set("processes", [{
+  this.set("processes", [Process.create({
     name: testName1
-  }, {
+  }), Process.create({
     name: testName2
-  }]);
+  })]);
 
   this.render(hbs`{{em-swimlane processes=processes}}`);
 
@@ -48,3 +50,37 @@ test('Basic creation test', function(assert) {
   assert.equal(this.$().text().trim().indexOf(testName1), 0);
   assert.notEqual(this.$().text().trim().indexOf(testName2), -1);
 });
+
+test('Normalization (Blocker based sorting) test - On a graph', function(assert) {
+  var p1 = Process.create({
+    name: "P1"
+  }),
+  p2 = Process.create({
+    name: "P2"
+  }),
+  p3 = Process.create({
+    name: "P3",
+    blockers: [p1, p2]
+  }),
+  p4 = Process.create({
+    name: "P4",
+    blockers: [p1]
+  }),
+  p5 = Process.create({
+    name: "P5",
+    blockers: [p3, p4]
+  });
+
+  this.set("processes", [p5, p4, p3, p2, p1]);
+
+  this.render(hbs`{{em-swimlane processes=processes}}`);
+
+  let names = this.$(".em-swimlane-process-name");
+
+  assert.equal(names.length, 5);
+  assert.equal(names.eq(0).text().trim(), p1.name);
+  assert.equal(names.eq(1).text().trim(), p4.name);
+  assert.equal(names.eq(2).text().trim(), p2.name);
+  assert.equal(names.eq(3).text().trim(), p3.name);
+  assert.equal(names.eq(4).text().trim(), p5.name);
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/tests/unit/controllers/dag/swimlane-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/unit/controllers/dag/swimlane-test.js b/tez-ui2/src/main/webapp/tests/unit/controllers/dag/swimlane-test.js
index 7886251..3013a23 100644
--- a/tez-ui2/src/main/webapp/tests/unit/controllers/dag/swimlane-test.js
+++ b/tez-ui2/src/main/webapp/tests/unit/controllers/dag/swimlane-test.js
@@ -38,4 +38,49 @@ test('Basic creation test', function(assert) {
   assert.ok(controller);
   assert.ok(controller.breadcrumbs);
   assert.ok(controller.columns);
+  assert.ok(controller.processes);
+  assert.ok(controller.eventBars);
+});
+
+test('Process test', function(assert) {
+
+  var vertices = [Ember.Object.create({
+    name: "v1"
+  }), Ember.Object.create({
+    name: "v2"
+  }), Ember.Object.create({
+    name: "v3"
+  }), Ember.Object.create({
+    name: "v4"
+  })];
+  vertices.firstObject = {
+    dag: {
+      edges: [{
+        inputVertexName: "v1",
+        outputVertexName: "v3"
+      }, {
+        inputVertexName: "v2",
+        outputVertexName: "v3"
+      }, {
+        inputVertexName: "v3",
+        outputVertexName: "v4"
+      }]
+    }
+  };
+
+  let controller = this.subject({
+    send: Ember.K,
+    beforeSort: {bind: Ember.K},
+    initVisibleColumns: Ember.K,
+    getCounterColumns: function () {
+      return [];
+    },
+    model: vertices
+  });
+
+  var processes = controller.get("processes");
+
+  assert.equal(processes[2].blockers[0].vertex, vertices[0]);
+  assert.equal(processes[2].blockers[1].vertex, vertices[1]);
+  assert.equal(processes[3].blockers[0].vertex, vertices[2]);
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/tests/unit/utils/process-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/unit/utils/process-test.js b/tez-ui2/src/main/webapp/tests/unit/utils/process-test.js
new file mode 100644
index 0000000..ffab868
--- /dev/null
+++ b/tez-ui2/src/main/webapp/tests/unit/utils/process-test.js
@@ -0,0 +1,147 @@
+/**
+ * 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 Process from '../../../utils/process';
+import { module, test } from 'qunit';
+
+module('Unit | Utility | process');
+
+test('Basic creation test', function(assert) {
+  let process = Process.create();
+
+  assert.ok(process);
+  assert.ok(process.getColor);
+  assert.ok(process.startEvent);
+  assert.ok(process.endEvent);
+  assert.ok(process.getAllBlockers);
+});
+
+test('getColor test', function(assert) {
+  let process = Process.create();
+
+  assert.equal(process.getColor(), "#0");
+
+  process.set("color", {
+    h: 10,
+    s: 20,
+    l: 30
+  });
+  assert.equal(process.getColor(), "hsl( 10, 20%, 30% )");
+  assert.equal(process.getColor(0.2), "hsl( 10, 20%, 40% )");
+});
+
+test('startEvent test', function(assert) {
+  let process = Process.create();
+
+  assert.equal(process.get("startEvent"), undefined);
+
+  process.set("events", [{
+    time: 50,
+  }, {
+    time: 70,
+  }, {
+    time: 20,
+  }, {
+    time: 80,
+  }]);
+  assert.equal(process.get("startEvent").time, 20);
+
+  process.set("events", [{
+    time: 50,
+  }, {
+    time: 70,
+  }, {
+    time: 80,
+  }]);
+  assert.equal(process.get("startEvent").time, 50);
+});
+
+test('endEvent test', function(assert) {
+  let process = Process.create();
+
+  assert.equal(process.get("endEvent"), undefined);
+
+  process.set("events", [{
+    time: 50,
+  }, {
+    time: 70,
+  }, {
+    time: 20,
+  }, {
+    time: 80,
+  }]);
+  assert.equal(process.get("endEvent").time, 80);
+
+  process.set("events", [{
+    time: 50,
+  }, {
+    time: 70,
+  }, {
+    time: 20,
+  }]);
+  assert.equal(process.get("endEvent").time, 70);
+});
+
+test('getAllBlockers test', function(assert) {
+  var cyclicProcess = Process.create({
+    name: "p3",
+  });
+  cyclicProcess.blockers = [cyclicProcess];
+
+  var multiLevelCycle1 = Process.create({
+    name: "p5",
+  });
+  var multiLevelCycle2 = Process.create({
+    name: "p6",
+  });
+  multiLevelCycle1.blockers = [multiLevelCycle2];
+  multiLevelCycle2.blockers = [multiLevelCycle1];
+
+  var process = Process.create({
+    blockers: [Process.create({
+      name: "p1"
+    }), Process.create({
+      name: "p2",
+      blockers: [Process.create({
+        name: "p21"
+      }), Process.create({
+        name: "p22",
+        blockers: [Process.create({
+          name: "p221"
+        })]
+      })]
+    }), cyclicProcess, Process.create({
+      name: "p4"
+    }), multiLevelCycle1]
+  });
+
+  var all = process.getAllBlockers();
+
+  assert.equal(all.length, 9);
+
+  assert.equal(all[0].get("name"), "p1");
+  assert.equal(all[1].get("name"), "p2");
+  assert.equal(all[2].get("name"), "p21");
+  assert.equal(all[3].get("name"), "p22");
+  assert.equal(all[4].get("name"), "p221");
+  assert.equal(all[5].get("name"), "p3");
+  assert.equal(all[6].get("name"), "p4");
+  assert.equal(all[7].get("name"), "p5");
+  assert.equal(all[8].get("name"), "p6");
+
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/c9c7d809/tez-ui2/src/main/webapp/tests/unit/utils/vertex-process-test.js
----------------------------------------------------------------------
diff --git a/tez-ui2/src/main/webapp/tests/unit/utils/vertex-process-test.js b/tez-ui2/src/main/webapp/tests/unit/utils/vertex-process-test.js
new file mode 100644
index 0000000..85c0d0f
--- /dev/null
+++ b/tez-ui2/src/main/webapp/tests/unit/utils/vertex-process-test.js
@@ -0,0 +1,98 @@
+/**
+ * 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 VertexProcess from '../../../utils/vertex-process';
+import { module, test } from 'qunit';
+
+import Ember from 'ember';
+
+module('Unit | Utility | vertex process');
+
+test('Basic creation test', function(assert) {
+  let process = VertexProcess.create();
+
+  assert.ok(process);
+
+  assert.ok(process.name);
+  assert.ok(process.completeTime);
+  assert.ok(process.blockingEventName);
+
+  assert.ok(process.events);
+  assert.ok(process.unblockTime);
+});
+
+test('unblockTime test', function(assert) {
+  var process = VertexProcess.create();
+  assert.equal(process.get("unblockTime"), undefined);
+
+  process = VertexProcess.create({
+    blockers: [VertexProcess.create({
+      vertex: {
+        endTime: 10
+      }
+    }), VertexProcess.create({
+      vertex: {
+        endTime: 15
+      }
+    }), VertexProcess.create({
+      vertex: {
+        endTime: 20
+      }
+    })]
+  });
+
+  assert.ok(process.get("unblockTime"), 20);
+
+  process.blockers[2].set("vertex", Ember.Object.create({
+    endTime: 12
+  }));
+  assert.ok(process.get("unblockTime"), 15);
+
+  process.blockers[2].vertex.set("endTime", 25);
+  assert.ok(process.get("unblockTime"), 25);
+});
+
+test('events test', function(assert) {
+  var process = VertexProcess.create({
+    vertex: Ember.Object.create({
+      events: [{
+        eventtype: "testEvent1"
+      },{
+        eventtype: "testEvent2"
+      }],
+      firstTaskStartTime: 10,
+      lastTaskFinishTime: 20
+    })
+  });
+
+  assert.equal(process.get("events.length"), 4);
+
+  assert.equal(process.get("events.0.name"), "testEvent1");
+  assert.equal(process.get("events.1.name"), "testEvent2");
+  assert.equal(process.get("events.2.time"), 10);
+  assert.equal(process.get("events.3.time"), 20);
+
+  process.set("blockers", [VertexProcess.create({
+    vertex: {
+      endTime: 30
+    }
+  })]);
+
+  assert.equal(process.get("events.length"), 5);
+  assert.equal(process.get("events.4.time"), 30);
+});