Repository: tez Updated Branches: refs/heads/master de7fd9aa5 -> bcf382ed9
TEZ-3063. Tez UI: Display Input, Output, Processor, Source and Sink configurations under a vertex (sree) Project: http://git-wip-us.apache.org/repos/asf/tez/repo Commit: http://git-wip-us.apache.org/repos/asf/tez/commit/bcf382ed Tree: http://git-wip-us.apache.org/repos/asf/tez/tree/bcf382ed Diff: http://git-wip-us.apache.org/repos/asf/tez/diff/bcf382ed Branch: refs/heads/master Commit: bcf382ed9bbedd25ba4a9cea074d99ef39da5093 Parents: de7fd9a Author: Sreenath Somarajapuram <s...@apache.org> Authored: Sat May 28 16:40:45 2016 +0530 Committer: Sreenath Somarajapuram <s...@apache.org> Committed: Sat May 28 16:40:45 2016 +0530 ---------------------------------------------------------------------- CHANGES.txt | 1 + .../src/main/webapp/app/controllers/vertex.js | 3 + .../webapp/app/controllers/vertex/configs.js | 183 +++++++++++ tez-ui/src/main/webapp/app/models/dag.js | 1 + tez-ui/src/main/webapp/app/router.js | 1 + .../src/main/webapp/app/routes/app/configs.js | 2 +- .../main/webapp/app/routes/vertex/configs.js | 37 +++ tez-ui/src/main/webapp/app/serializers/dag.js | 1 + tez-ui/src/main/webapp/app/styles/app.less | 1 + tez-ui/src/main/webapp/app/styles/colors.less | 6 +- .../main/webapp/app/styles/details-page.less | 7 + .../webapp/app/styles/vertex-configs-page.less | 101 ++++++ .../webapp/app/templates/vertex/configs.hbs | 189 +++++++++++ tez-ui/src/main/webapp/package.json | 3 +- .../webapp/tests/unit/controllers/app-test.js | 4 +- .../tests/unit/controllers/attempt-test.js | 2 + .../webapp/tests/unit/controllers/dag-test.js | 2 + .../webapp/tests/unit/controllers/task-test.js | 2 + .../tests/unit/controllers/vertex-test.js | 2 + .../unit/controllers/vertex/configs-test.js | 310 +++++++++++++++++++ .../main/webapp/tests/unit/models/dag-test.js | 1 + .../tests/unit/routes/vertex/configs-test.js | 46 +++ 22 files changed, 899 insertions(+), 6 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index ed41b07..f520ee8 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -40,6 +40,7 @@ ALL CHANGES: TEZ-3255. Tez UI: Hide swimlane while displaying running DAGs from old versions of Tez TEZ-3259. Tez UI: Build issue - File saver package is not working well with bower TEZ-3262. Tez UI : zip.js is not having a bower friendly versioning system + TEZ-3063. Tez UI: Display Input, Output, Processor, Source and Sink configurations under a vertex Release 0.8.4: Unreleased http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/controllers/vertex.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/controllers/vertex.js b/tez-ui/src/main/webapp/app/controllers/vertex.js index 78f5db9..10d992a 100644 --- a/tez-ui/src/main/webapp/app/controllers/vertex.js +++ b/tez-ui/src/main/webapp/app/controllers/vertex.js @@ -48,5 +48,8 @@ export default ParentController.extend({ }, { text: "Task Attempts", routeName: "vertex.attempts" + }, { + text: "Configurations", + routeName: "vertex.configs" }] }); http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/controllers/vertex/configs.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/controllers/vertex/configs.js b/tez-ui/src/main/webapp/app/controllers/vertex/configs.js new file mode 100644 index 0000000..1cf4a3d --- /dev/null +++ b/tez-ui/src/main/webapp/app/controllers/vertex/configs.js @@ -0,0 +1,183 @@ +/*global more*/ +/** + * 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 TableController from '../table'; +import ColumnDefinition from 'em-table/utils/column-definition'; + +var MoreObject = more.Object; + +// Better fits in more-js +function arrayfy(object) { + var array = []; + MoreObject.forEach(object, function (key, value) { + array.push({ + key: key, + value: value + }); + }); + return array; +} + +export default TableController.extend({ + searchText: "tez", + + queryParams: ["configType", "configID"], + configType: null, + configID: null, + + breadcrumbs: Ember.computed("configDetails", "configID", "configType", function () { + var crumbs = [{ + text: "Configurations", + routeName: "vertex.configs", + queryParams: { + configType: null, + configID: null, + } + }], + type = this.get("configType"), + name; + + if(this.get("configType")) { + name = this.get("configDetails.name") || this.get("configDetails.desc"); + } + + if(type && name) { + type = type.capitalize(); + crumbs.push({ + text: `${type} [ ${name} ]`, + routeName: "vertex.configs", + }); + } + + return crumbs; + }), + + setBreadcrumbs: function() { + this._super(); + Ember.run.later(this, "send", "bubbleBreadcrumbs", []); + }, + + columns: ColumnDefinition.make([{ + id: 'configName', + headerTitle: 'Configuration Name', + contentPath: 'configName', + }, { + id: 'configValue', + headerTitle: 'Configuration Value', + contentPath: 'configValue', + }]), + + normalizeConfig: function (config) { + var userPayload = config.userPayloadAsText ? JSON.parse(config.userPayloadAsText) : {}; + return { + id: config.name || null, + name: config.name, + desc: userPayload.desc, + class: config.class || config.processorClass, + initializer: config.initializer, + configs: arrayfy(userPayload.config || {}) + }; + }, + + configsHash: Ember.computed("model.name", "model.dag.vertices", function () { + var vertexName = this.get("model.name"), + + inputConfigs = [], + outputConfigs = [], + vertexDetails; + + if(!this.get("model")) { + return {}; + } + + vertexDetails = this.get("model.dag.vertices").findBy("vertexName", vertexName); + + (this.get("model.dag.edges") || []).forEach(function (edge) { + if(edge.outputVertexName === vertexName) { + let payload = edge.outputUserPayloadAsText; + inputConfigs.push({ + id: edge.edgeId, + desc: `From ${edge.inputVertexName}`, + class: edge.edgeDestinationClass, + configs: arrayfy(payload ? Ember.get(JSON.parse(payload), "config") : {}) + }); + } + else if(edge.inputVertexName === vertexName) { + let payload = edge.inputUserPayloadAsText; + outputConfigs.push({ + id: edge.edgeId, + desc: `To ${edge.outputVertexName}`, + class: edge.edgeSourceClass, + configs: arrayfy(payload ? Ember.get(JSON.parse(payload), "config") : {}) + }); + } + }); + + return { + processor: this.normalizeConfig(vertexDetails), + + sources: (vertexDetails.additionalInputs || []).map(this.normalizeConfig), + sinks: (vertexDetails.additionalOutputs || []).map(this.normalizeConfig), + + inputs: inputConfigs, + outputs: outputConfigs + }; + }), + + configDetails: Ember.computed("configsHash", "configType", "configID", function () { + var configType = this.get("configType"), + details; + + if(configType) { + details = Ember.get(this.get("configsHash"), configType); + } + + if(Array.isArray(details)) { + details = details.findBy("id", this.get("configID")); + } + + return details; + }), + + configs: Ember.computed("configDetails", function () { + var configs = this.get("configDetails.configs"); + + if(Array.isArray(configs)) { + return Ember.A(configs.map(function (config) { + return Ember.Object.create({ + configName: config.key, + configValue: config.value + }); + })); + } + }), + + actions: { + showConf: function (type, details) { + this.setProperties({ + configType: type, + configID: details.id + }); + Ember.run.later(this, "send", "bubbleBreadcrumbs", []); + } + } + +}); http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/models/dag.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/models/dag.js b/tez-ui/src/main/webapp/app/models/dag.js index 84509e1..5e011e2 100644 --- a/tez-ui/src/main/webapp/app/models/dag.js +++ b/tez-ui/src/main/webapp/app/models/dag.js @@ -63,6 +63,7 @@ export default AMTimelineModel.extend({ }), vertexIdNameMap: DS.attr("object"), + vertexNameIdMap: DS.attr("object"), callerID: DS.attr("string"), callerContext: DS.attr("string"), http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/router.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/router.js b/tez-ui/src/main/webapp/app/router.js index 98a456a..7f18ae6 100644 --- a/tez-ui/src/main/webapp/app/router.js +++ b/tez-ui/src/main/webapp/app/router.js @@ -38,6 +38,7 @@ Router.map(function() { this.route('tasks'); this.route('attempts'); this.route('counters'); + this.route('configs'); }); this.route('task', {path: '/task/:task_id'}, function() { this.route('attempts'); http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/routes/app/configs.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/routes/app/configs.js b/tez-ui/src/main/webapp/app/routes/app/configs.js index bdd53ae..c58f2f1 100644 --- a/tez-ui/src/main/webapp/app/routes/app/configs.js +++ b/tez-ui/src/main/webapp/app/routes/app/configs.js @@ -20,7 +20,7 @@ import Ember from 'ember'; import SingleAmPollsterRoute from '../single-am-pollster'; export default SingleAmPollsterRoute.extend({ - title: "Application Details", + title: "Application Configurations", loaderNamespace: "app", http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/routes/vertex/configs.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/routes/vertex/configs.js b/tez-ui/src/main/webapp/app/routes/vertex/configs.js new file mode 100644 index 0000000..a131896 --- /dev/null +++ b/tez-ui/src/main/webapp/app/routes/vertex/configs.js @@ -0,0 +1,37 @@ +/** + * 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 SingleAmPollsterRoute from '../single-am-pollster'; + +export default SingleAmPollsterRoute.extend({ + title: "Vertex Configurations", + + loaderNamespace: "vertex", + + canPoll: false, + + setupController: function (controller, model) { + this._super(controller, model); + Ember.run.later(this, "startCrumbBubble"); + }, + + load: function (value, query, options) { + return this.get("loader").queryRecord('vertex', this.modelFor("vertex").get("id"), options); + }, +}); http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/serializers/dag.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/serializers/dag.js b/tez-ui/src/main/webapp/app/serializers/dag.js index a64bfe1..bf31174 100644 --- a/tez-ui/src/main/webapp/app/serializers/dag.js +++ b/tez-ui/src/main/webapp/app/serializers/dag.js @@ -125,6 +125,7 @@ export default TimelineSerializer.extend({ containerLogs: getContainerLogs, vertexIdNameMap: getIdNameMap, + vertexNameIdMap: 'otherinfo.vertexNameIdMapping', callerID: 'primaryfilters.callerId.0', callerContext: 'callerContext', http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/styles/app.less ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/styles/app.less b/tez-ui/src/main/webapp/app/styles/app.less index c881553..b4a4c47 100644 --- a/tez-ui/src/main/webapp/app/styles/app.less +++ b/tez-ui/src/main/webapp/app/styles/app.less @@ -50,3 +50,4 @@ @import "page-layout"; @import "details-page"; @import "swimlane-page"; +@import "vertex-configs-page"; http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/styles/colors.less ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/styles/colors.less b/tez-ui/src/main/webapp/app/styles/colors.less index af470ff..bc4a398 100644 --- a/tez-ui/src/main/webapp/app/styles/colors.less +++ b/tez-ui/src/main/webapp/app/styles/colors.less @@ -19,14 +19,14 @@ // Colors @logo-orange: #D27A22; -@bg-lite: #f5f5f5; +@bg-lite: #f0f0f0; @bg-liter: #f5f5f5; @bg-red-light: #FFE6E6; @bg-grey: #f0f0f0; @border-lite: #e5e5e5; -@border-color: #dcdcdc; +@border-color: #ddd; @white: #fff; @@ -41,4 +41,4 @@ @success-color: limegreen; @error-color: crimson; @warning-color: orange; -@unknown-color: crimson; \ No newline at end of file +@unknown-color: crimson; http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/styles/details-page.less ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/styles/details-page.less b/tez-ui/src/main/webapp/app/styles/details-page.less index 75d5e11..54380e2 100644 --- a/tez-ui/src/main/webapp/app/styles/details-page.less +++ b/tez-ui/src/main/webapp/app/styles/details-page.less @@ -22,6 +22,10 @@ margin: 0 10px 10px 0; + &:first-child { + margin: 0 0 10px 0; + } + table-layout: fixed; .progress { @@ -40,7 +44,10 @@ td { padding: 0px 20px 0px 0px; + + max-width: 600px; white-space: nowrap; + overflow: auto; .ember-view { display: inline; http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/styles/vertex-configs-page.less ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/styles/vertex-configs-page.less b/tez-ui/src/main/webapp/app/styles/vertex-configs-page.less new file mode 100644 index 0000000..ffa1bdd --- /dev/null +++ b/tez-ui/src/main/webapp/app/styles/vertex-configs-page.less @@ -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. + */ + +.vertex-configs-container { + display: inline-block; + width: 40%; + vertical-align: top; + + .row-container { + display: flex; + } + + .column-container { + display: inline-block; + } + + .column-container, .processor { + margin: 0 5px 0 5px; + flex: 1 1; + + .box { + border: 1px solid @border-color; + border-radius: 5px; + overflow: hidden; + + .header, .config-cell { + padding: 5px; + } + + .header { + height: auto; + margin: 0px; + } + + .config-cell { + cursor: pointer; + + border-top: 1px dotted @border-color; + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + .count-txt { + color: @text-light; + font-size: .8em; + } + + &:nth-child(2) { + border-top: none; + } + + &.selected { + background-color: @bg-liter; + } + + &:hover { + background-color: @bg-lite; + } + } + } + + .link { + border-left: 1px solid @border-color; + margin-left: 50%; + height: 10px; + } + } + + .top-column { + display: flex; + flex-direction: column; + justify-content: flex-end; + } + + .processor { + text-align: center; + } + +} + +.configuration-details { + display: inline-block; + width: 60%; + padding-left: 10px; +} http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/templates/vertex/configs.hbs ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/app/templates/vertex/configs.hbs b/tez-ui/src/main/webapp/app/templates/vertex/configs.hbs new file mode 100644 index 0000000..de6d3a9 --- /dev/null +++ b/tez-ui/src/main/webapp/app/templates/vertex/configs.hbs @@ -0,0 +1,189 @@ +{{! + * 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}} + + <!-- Vertex configuration visualization --> + <div class="vertex-configs-container"> + <div class="row-container"> + {{#if configsHash.sources.length}} + <div class="column-container top-column"> + <div class="box"> + <div class="header">Sources</div> + {{#each configsHash.sources as |source|}} + <div class="config-cell {{if (eq source configDetails) 'selected'}}" + {{action 'showConf' 'sources' source}}> + {{source.name}} + {{#if source.desc}} + [ {{source.desc}} ] + {{/if}} + <div class="count-txt"> + {{#if source.configs.length}} + Configurations: {{source.configs.length}} + {{else}} + Configuration not available! + {{/if}} + </div> + </div> + {{/each}} + </div> + <div class="link"></div> + </div> + {{/if}} + {{#if configsHash.inputs.length}} + <div class="column-container top-column"> + <div class="box"> + <div class="header">Inputs</div> + {{#each configsHash.inputs as |input|}} + <div class="config-cell {{if (eq input configDetails) 'selected'}}" + {{action 'showConf' 'inputs' input}}> + {{input.desc}} + <div class="count-txt"> + {{#if input.configs.length}} + Configurations: {{input.configs.length}} + {{else}} + Configuration not available! + {{/if}} + </div> + </div> + {{/each}} + </div> + <div class="link"></div> + </div> + {{/if}} + </div> + <div class="processor"> + <div class="box"> + <div class="header">Processor</div> + <div class="config-cell {{if (eq configsHash.processor configDetails) 'selected'}}" + {{action 'showConf' 'processor' configsHash.processor}}> + {{configsHash.processor.desc}} + <div class="count-txt"> + {{#if configsHash.processor.configs.length}} + Configurations: {{configsHash.processor.configs.length}} + {{else}} + Configuration not available! + {{/if}} + </div> + </div> + </div> + </div> + <div class="row-container"> + {{#if configsHash.sinks.length}} + <div class="column-container"> + <div class="link"></div> + <div class="box"> + <div class="header">Sinks</div> + {{#each configsHash.sinks as |sink|}} + <div class="config-cell {{if (eq sink configDetails) 'selected'}}" + {{action 'showConf' 'sinks' sink}}> + {{sink.name}} + {{#if sink.desc}} + [ {{sink.desc}} ] + {{/if}} + <div class="count-txt"> + {{#if sink.configs.length}} + Configurations: {{sink.configs.length}} + {{else}} + Configuration not available! + {{/if}} + </div> + </div> + {{/each}} + </div> + </div> + {{/if}} + {{#if configsHash.outputs.length}} + <div class="column-container"> + <div class="link"></div> + <div class="box"> + <div class="header">Outputs</div> + {{#each configsHash.outputs as |output|}} + <div class="config-cell {{if (eq output configDetails) 'selected'}}" + {{action 'showConf' 'outputs' output}}> + {{output.desc}} + <div class="count-txt"> + {{#if output.configs.length}} + Configurations: {{output.configs.length}} + {{else}} + Configuration not available! + {{/if}} + </div> + </div> + {{/each}} + </div> + </div> + {{/if}} + </div> + </div><div class="configuration-details"> + + {{#if configType}} + <!-- Configuration details display --> + <table class='detail-list'> + <thead> + <tr> + <th colspan=2>Details</th> + </tr> + </thead> + <tbody> + {{#if configDetails.name}} + <tr> + <td>Name</td> + <td>{{configDetails.name}}</td> + </tr> + {{/if}} + {{#if configDetails.desc}} + <tr> + <td>Description</td> + <td>{{configDetails.desc}}</td> + </tr> + {{/if}} + {{#if configDetails.class}} + <tr> + <td>Class</td> + <td>{{configDetails.class}}</td> + </tr> + {{/if}} + {{#if configDetails.initializer}} + <tr> + <td>Initializer</td> + <td>{{configDetails.initializer}}</td> + </tr> + {{/if}} + </tbody> + </table> + + <!-- Configurations display --> + {{em-table + columns=columns + rows=configs + + rowCount=configs.length + definition=definition + + enablePagination=false + + searchAction="searchChanged" + sortAction="sortChanged" + }} + {{/if}} + </div> + +{{else}} + {{partial "loading"}} +{{/if}} http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/package.json ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/package.json b/tez-ui/src/main/webapp/package.json index accddec..7916d85 100644 --- a/tez-ui/src/main/webapp/package.json +++ b/tez-ui/src/main/webapp/package.json @@ -53,10 +53,11 @@ "ember-disable-proxy-controllers": "1.0.1", "ember-export-application-global": "1.0.5", "ember-resolver": "2.0.3", + "ember-truth-helpers": "1.2.0", "phantomjs": "1.9.19" }, "dependencies": { - "em-helpers": "0.5.8", + "em-helpers": "0.5.9", "em-table": "0.3.12", "em-tgraph": "0.0.5" } http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/tests/unit/controllers/app-test.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/tests/unit/controllers/app-test.js b/tez-ui/src/main/webapp/tests/unit/controllers/app-test.js index 2fc7276..f969d7e 100644 --- a/tez-ui/src/main/webapp/tests/unit/controllers/app-test.js +++ b/tez-ui/src/main/webapp/tests/unit/controllers/app-test.js @@ -20,7 +20,7 @@ import Ember from 'ember'; import { moduleFor, test } from 'ember-qunit'; -moduleFor('controller:dag', 'Unit | Controller | dag', { +moduleFor('controller:app', 'Unit | Controller | app', { // Specify the other units that are required for this test. // needs: ['controller:foo'] }); @@ -34,4 +34,6 @@ test('Basic creation test', function(assert) { assert.ok(controller); assert.ok(controller.breadcrumbs); assert.ok(controller.tabs); + + assert.equal(controller.tabs.length, 3); }); http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/tests/unit/controllers/attempt-test.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/tests/unit/controllers/attempt-test.js b/tez-ui/src/main/webapp/tests/unit/controllers/attempt-test.js index da451f7..4053470 100644 --- a/tez-ui/src/main/webapp/tests/unit/controllers/attempt-test.js +++ b/tez-ui/src/main/webapp/tests/unit/controllers/attempt-test.js @@ -34,4 +34,6 @@ test('Basic creation test', function(assert) { assert.ok(controller); assert.ok(controller.breadcrumbs); assert.ok(controller.tabs); + + assert.equal(controller.tabs.length, 2); }); http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/tests/unit/controllers/dag-test.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/tests/unit/controllers/dag-test.js b/tez-ui/src/main/webapp/tests/unit/controllers/dag-test.js index 2fc7276..f40cd48 100644 --- a/tez-ui/src/main/webapp/tests/unit/controllers/dag-test.js +++ b/tez-ui/src/main/webapp/tests/unit/controllers/dag-test.js @@ -34,4 +34,6 @@ test('Basic creation test', function(assert) { assert.ok(controller); assert.ok(controller.breadcrumbs); assert.ok(controller.tabs); + + assert.equal(controller.tabs.length, 7); }); http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/tests/unit/controllers/task-test.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/tests/unit/controllers/task-test.js b/tez-ui/src/main/webapp/tests/unit/controllers/task-test.js index c9cee79..98c338b 100644 --- a/tez-ui/src/main/webapp/tests/unit/controllers/task-test.js +++ b/tez-ui/src/main/webapp/tests/unit/controllers/task-test.js @@ -34,4 +34,6 @@ test('Basic creation test', function(assert) { assert.ok(controller); assert.ok(controller.breadcrumbs); assert.ok(controller.tabs); + + assert.equal(controller.tabs.length, 3); }); http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/tests/unit/controllers/vertex-test.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/tests/unit/controllers/vertex-test.js b/tez-ui/src/main/webapp/tests/unit/controllers/vertex-test.js index e8e9b3f..2283110 100644 --- a/tez-ui/src/main/webapp/tests/unit/controllers/vertex-test.js +++ b/tez-ui/src/main/webapp/tests/unit/controllers/vertex-test.js @@ -34,4 +34,6 @@ test('Basic creation test', function(assert) { assert.ok(controller); assert.ok(controller.breadcrumbs); assert.ok(controller.tabs); + + assert.equal(controller.tabs.length, 5); }); http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/tests/unit/controllers/vertex/configs-test.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/tests/unit/controllers/vertex/configs-test.js b/tez-ui/src/main/webapp/tests/unit/controllers/vertex/configs-test.js new file mode 100644 index 0000000..89015a5 --- /dev/null +++ b/tez-ui/src/main/webapp/tests/unit/controllers/vertex/configs-test.js @@ -0,0 +1,310 @@ +/** + * 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:vertex/configs', 'Unit | Controller | vertex/configs', { + // 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 + }); + + assert.ok(controller); + + assert.ok(controller.breadcrumbs); + assert.ok(controller.setBreadcrumbs); + + assert.ok(controller.columns); + assert.equal(controller.columns.length, 2); + + assert.ok(controller.normalizeConfig); + assert.ok(controller.configsHash); + assert.ok(controller.configDetails); + assert.ok(controller.configs); + + assert.ok(controller.actions.showConf); + + assert.equal(controller.searchText, "tez"); + assert.notEqual(controller.queryParams.indexOf("configType"), -1); + assert.notEqual(controller.queryParams.indexOf("configID"), -1); +}); + +test('Breadcrumbs test', function(assert) { + let controller = this.subject({ + send: Ember.K, + initVisibleColumns: Ember.K, + configDetails: { + name: "name" + } + }); + + assert.equal(controller.get("breadcrumbs").length, 1); + assert.equal(controller.get("breadcrumbs")[0].text, "Configurations"); + assert.equal(controller.get("breadcrumbs")[0].queryParams.configType, null); + assert.equal(controller.get("breadcrumbs")[0].queryParams.configID, null); + + controller.setProperties({ + configType: "TestType", + configID: "ID", + }); + assert.equal(controller.get("breadcrumbs").length, 2); + assert.equal(controller.get("breadcrumbs")[1].text, "TestType [ name ]"); +}); + +test('normalizeConfig test', function(assert) { + let controller = this.subject({ + send: Ember.K, + initVisibleColumns: Ember.K, + }), + testName = "name", + testClass = "TestClass", + testInit = "TestInit", + payload = { + desc: 'abc', + config: { + x:1, + y:2 + } + }, + config; + + // Processor + config = controller.normalizeConfig({ + processorClass: testClass, + userPayloadAsText: JSON.stringify(payload) + }); + assert.equal(config.id, null); + assert.equal(config.class, testClass); + assert.equal(config.desc, payload.desc); + assert.deepEqual(config.configs, [{key: "x", value: 1}, {key: "y", value: 2}]); + + // Inputs & outputs + config = controller.normalizeConfig({ + name: testName, + class: testClass, + initializer: testInit, + userPayloadAsText: JSON.stringify(payload) + }); + assert.equal(config.id, testName); + assert.equal(config.class, testClass); + assert.equal(config.initializer, testInit); + assert.equal(config.desc, payload.desc); + assert.deepEqual(config.configs, [{key: "x", value: 1}, {key: "y", value: 2}]); +}); + +test('configsHash test', function(assert) { + let controller = this.subject({ + send: Ember.K, + initVisibleColumns: Ember.K, + }); + + assert.deepEqual(controller.get("configsHash"), {}); + + controller.set("model", { + dag: { + vertices: [ + { + "vertexName": "v1", + "processorClass": "org.apache.tez.mapreduce.processor.map.MapProcessor", + "userPayloadAsText": "{\"desc\":\"Tokenizer Vertex\",\"config\":{\"config.key\":\"11\"}}", + "additionalInputs": [ + { + "name": "MRInput", + "class": "org.apache.tez.mapreduce.input.MRInputLegacy", + "initializer": "org.apache.tez.mapreduce.common.MRInputAMSplitGenerator", + "userPayloadAsText": "{\"desc\":\"HDFS Input\",\"config\":{\"config.key\":\"22\"}}" + } + ] + }, + { + "vertexName": "v2", + "processorClass": "org.apache.tez.mapreduce.processor.reduce.ReduceProcessor", + "userPayloadAsText": "{\"desc\":\"Summation Vertex\",\"config\":{\"config.key\":\"33\"}}" + }, + { + "vertexName": "v3", + "processorClass": "org.apache.tez.mapreduce.processor.reduce.ReduceProcessor", + "userPayloadAsText": "{\"desc\":\"Sorter Vertex\",\"config\":{\"config.key1\":\"44\", \"config.key2\":\"444\"}}", + "additionalOutputs": [ + { + "name": "MROutput", + "class": "org.apache.tez.mapreduce.output.MROutputLegacy", + "initializer": "org.apache.tez.mapreduce.committer.MROutputCommitter", + "userPayloadAsText": "{\"desc\":\"HDFS Output\",\"config\":{\"config.key\":\"55\"}}" + } + ] + } + ], + edges: [ + { + "edgeId": "edg1", + "inputVertexName": "v2", + "outputVertexName": "v3", + "edgeSourceClass": "org.apache.tez.runtime.library.output.OrderedPartitionedKVOutput", + "edgeDestinationClass": "org.apache.tez.runtime.library.input.OrderedGroupedInputLegacy", + "outputUserPayloadAsText": "{\"config\":{\"config.key\":\"66\"}}", + "inputUserPayloadAsText": "{\"config\":{\"config.key\":\"77\"}}", + }, + { + "edgeId": "edg2", + "inputVertexName": "v1", + "outputVertexName": "v2", + "edgeSourceClass": "org.apache.tez.runtime.library.output.OrderedPartitionedKVOutput", + "edgeDestinationClass": "org.apache.tez.runtime.library.input.OrderedGroupedInputLegacy", + "outputUserPayloadAsText": "{\"config\":{\"config.key\":\"88\"}}", + "inputUserPayloadAsText": "{\"config\":{\"config.key\":\"99\"}}", + } + ] + } + }); + + // Test for vertex v1 + controller.set("model.name", "v1"); + + assert.ok(controller.get("configsHash.processor")); + assert.equal(controller.get("configsHash.processor.name"), null); + assert.equal(controller.get("configsHash.processor.desc"), "Tokenizer Vertex"); + assert.equal(controller.get("configsHash.processor.class"), "org.apache.tez.mapreduce.processor.map.MapProcessor"); + assert.equal(controller.get("configsHash.processor.configs.length"), 1); + assert.equal(controller.get("configsHash.processor.configs.0.key"), "config.key"); + assert.equal(controller.get("configsHash.processor.configs.0.value"), 11); + + assert.ok(controller.get("configsHash.sources")); + assert.equal(controller.get("configsHash.sources.length"), 1); + assert.equal(controller.get("configsHash.sources.0.name"), "MRInput"); + assert.equal(controller.get("configsHash.sources.0.desc"), "HDFS Input"); + assert.equal(controller.get("configsHash.sources.0.class"), "org.apache.tez.mapreduce.input.MRInputLegacy"); + assert.equal(controller.get("configsHash.sources.0.initializer"), "org.apache.tez.mapreduce.common.MRInputAMSplitGenerator"); + assert.equal(controller.get("configsHash.sources.0.configs.length"), 1); + assert.equal(controller.get("configsHash.sources.0.configs.0.key"), "config.key"); + assert.equal(controller.get("configsHash.sources.0.configs.0.value"), 22); + + assert.ok(controller.get("configsHash.sinks")); + assert.equal(controller.get("configsHash.sinks.length"), 0); + + assert.ok(controller.get("configsHash.inputs")); + assert.equal(controller.get("configsHash.inputs.length"), 0); + + assert.ok(controller.get("configsHash.outputs")); + assert.equal(controller.get("configsHash.outputs.length"), 1); + assert.equal(controller.get("configsHash.outputs.0.name"), null); + assert.equal(controller.get("configsHash.outputs.0.desc"), "To v2"); + assert.equal(controller.get("configsHash.outputs.0.class"), "org.apache.tez.runtime.library.output.OrderedPartitionedKVOutput"); + assert.equal(controller.get("configsHash.outputs.0.configs.length"), 1); + assert.equal(controller.get("configsHash.outputs.0.configs.0.key"), "config.key"); + assert.equal(controller.get("configsHash.outputs.0.configs.0.value"), 99); + + // Test for vertex v3 + controller.set("model.name", "v3"); + + assert.ok(controller.get("configsHash.processor")); + assert.equal(controller.get("configsHash.processor.name"), null); + assert.equal(controller.get("configsHash.processor.desc"), "Sorter Vertex"); + assert.equal(controller.get("configsHash.processor.class"), "org.apache.tez.mapreduce.processor.reduce.ReduceProcessor"); + assert.equal(controller.get("configsHash.processor.configs.length"), 2); + assert.equal(controller.get("configsHash.processor.configs.0.key"), "config.key1"); + assert.equal(controller.get("configsHash.processor.configs.0.value"), 44); + assert.equal(controller.get("configsHash.processor.configs.1.key"), "config.key2"); + assert.equal(controller.get("configsHash.processor.configs.1.value"), 444); + + assert.ok(controller.get("configsHash.sources")); + assert.equal(controller.get("configsHash.sources.length"), 0); + + assert.ok(controller.get("configsHash.sinks")); + assert.equal(controller.get("configsHash.sinks.length"), 1); + assert.equal(controller.get("configsHash.sinks.0.name"), "MROutput"); + assert.equal(controller.get("configsHash.sinks.0.desc"), "HDFS Output"); + assert.equal(controller.get("configsHash.sinks.0.class"), "org.apache.tez.mapreduce.output.MROutputLegacy"); + assert.equal(controller.get("configsHash.sinks.0.initializer"), "org.apache.tez.mapreduce.committer.MROutputCommitter"); + assert.equal(controller.get("configsHash.sinks.0.configs.length"), 1); + assert.equal(controller.get("configsHash.sinks.0.configs.0.key"), "config.key"); + assert.equal(controller.get("configsHash.sinks.0.configs.0.value"), 55); + + assert.ok(controller.get("configsHash.inputs")); + assert.equal(controller.get("configsHash.inputs.length"), 1); + assert.equal(controller.get("configsHash.inputs.0.name"), null); + assert.equal(controller.get("configsHash.inputs.0.desc"), "From v2"); + assert.equal(controller.get("configsHash.inputs.0.class"), "org.apache.tez.runtime.library.input.OrderedGroupedInputLegacy"); + assert.equal(controller.get("configsHash.inputs.0.configs.length"), 1); + assert.equal(controller.get("configsHash.inputs.0.configs.0.key"), "config.key"); + assert.equal(controller.get("configsHash.inputs.0.configs.0.value"), 66); + + assert.ok(controller.get("configsHash.outputs")); + assert.equal(controller.get("configsHash.outputs.length"), 0); + +}); + +test('configDetails test', function(assert) { + let configsHash = { + type: [{ + id: "id1" + },{ + id: "id2" + }] + }, + controller = this.subject({ + send: Ember.K, + initVisibleColumns: Ember.K, + configsHash: configsHash + }); + + assert.equal(controller.get("configDetails"), undefined); + + controller.set("configType", "random"); + assert.equal(controller.get("configDetails"), undefined); + + controller.set("configType", "type"); + assert.equal(controller.get("configDetails"), undefined); + + controller.set("configID", "id1"); + assert.equal(controller.get("configDetails"), configsHash.type[0]); + + controller.set("configID", "id2"); + assert.equal(controller.get("configDetails"), configsHash.type[1]); +}); + +test('configs test', function(assert) { + let controller = this.subject({ + send: Ember.K, + initVisibleColumns: Ember.K, + configDetails: { + configs: [{ + key: "x", + value: 1 + }, { + key: "y", + value: 2 + }] + } + }); + + assert.equal(controller.get("configs").length, 2); + assert.ok(controller.get("configs.0") instanceof Ember.Object); + + assert.equal(controller.get("configs.0.configName"), "x"); + assert.equal(controller.get("configs.0.configValue"), 1); + assert.equal(controller.get("configs.1.configName"), "y"); + assert.equal(controller.get("configs.1.configValue"), 2); +}); http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/tests/unit/models/dag-test.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/tests/unit/models/dag-test.js b/tez-ui/src/main/webapp/tests/unit/models/dag-test.js index 78affcf..2c07f7b 100644 --- a/tez-ui/src/main/webapp/tests/unit/models/dag-test.js +++ b/tez-ui/src/main/webapp/tests/unit/models/dag-test.js @@ -49,6 +49,7 @@ test('Basic creation test', function(assert) { assert.ok(model.containerLogs); assert.ok(model.vertexIdNameMap); + assert.ok(model.vertexNameIdMap); assert.ok(model.callerID); assert.ok(model.callerContext); http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/tests/unit/routes/vertex/configs-test.js ---------------------------------------------------------------------- diff --git a/tez-ui/src/main/webapp/tests/unit/routes/vertex/configs-test.js b/tez-ui/src/main/webapp/tests/unit/routes/vertex/configs-test.js new file mode 100644 index 0000000..e293a7e --- /dev/null +++ b/tez-ui/src/main/webapp/tests/unit/routes/vertex/configs-test.js @@ -0,0 +1,46 @@ +/** + * 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:vertex/configs', 'Unit | Route | vertex/configs', { + // 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); +}); + +test('setupController test', function(assert) { + assert.expect(1); + + let route = this.subject({ + startCrumbBubble: function () { + assert.ok(true); + } + }); + + route.setupController({}, {}); +});