mirror of
https://github.com/ansible/awx.git
synced 2024-11-01 08:21:15 +03:00
Moves network_ui_test to an external repo
This commit is contained in:
parent
abb95fdad6
commit
96b3ebd31e
@ -1,6 +1,5 @@
|
|||||||
from channels.routing import route
|
from channels.routing import route
|
||||||
from awx.network_ui.routing import channel_routing as network_ui_routing
|
from awx.network_ui.routing import channel_routing as network_ui_routing
|
||||||
from awx.network_ui_test.routing import channel_routing as network_ui_test_routing
|
|
||||||
|
|
||||||
|
|
||||||
channel_routing = [
|
channel_routing = [
|
||||||
@ -11,4 +10,3 @@ channel_routing = [
|
|||||||
|
|
||||||
|
|
||||||
channel_routing += network_ui_routing
|
channel_routing += network_ui_routing
|
||||||
channel_routing += network_ui_test_routing
|
|
||||||
|
@ -1,221 +0,0 @@
|
|||||||
|
|
||||||
Network UI Test
|
|
||||||
===============
|
|
||||||
|
|
||||||
Network UI Test is an event driven test framework for testing the Network UI.
|
|
||||||
This tool works by setting up the UI with a pre-test run snapshot, replaying a
|
|
||||||
set of events to the Network UI, catching exceptions and then comparing the
|
|
||||||
state of the system after the events to a post test run snapshot. Test results
|
|
||||||
and test code coverage are stored for each test run through the system. This
|
|
||||||
allows us to determine which lines of test are run during each test and which
|
|
||||||
lines are run under any test. This can be very helpful during development to
|
|
||||||
determine that the code is executed as expected given a certain input, to find
|
|
||||||
code that needs additional tests, and to find code that is not be run under any
|
|
||||||
of the given inputs (a.k.a. dead code).
|
|
||||||
|
|
||||||
Using this test framework it is fairly easy to achieve 90%+ code coverage under
|
|
||||||
test with a few days of work recording and crafting tests.
|
|
||||||
|
|
||||||
Test Steps
|
|
||||||
----------
|
|
||||||
|
|
||||||
The tests suppported by this test framework perform the following steps:
|
|
||||||
|
|
||||||
* Reset code coverage records
|
|
||||||
* Recreate a pre-test snapshot on the UI
|
|
||||||
* Replay a set of events to the UI
|
|
||||||
* Check for exceptions thrown by the UI
|
|
||||||
* Check the state of the system based on the post-test snapshot
|
|
||||||
* Report the pass/fail/error status of the test by test name, code version, and client id
|
|
||||||
* Report the code coverage per test result
|
|
||||||
* Repeat for next test
|
|
||||||
|
|
||||||
Test Case Data Schema
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
The tests are completely data driven and the data for the test snapshots and events are stored in a JSON
|
|
||||||
structure in the `TestCase` model under the `test_case_data` field that has the following structure:
|
|
||||||
|
|
||||||
{
|
|
||||||
"event_trace": [],
|
|
||||||
"fsm_trace": [],
|
|
||||||
"snapshots": [
|
|
||||||
{
|
|
||||||
"devices": [],
|
|
||||||
"inventory_toolbox": [],
|
|
||||||
"links": [],
|
|
||||||
"message_id": 4,
|
|
||||||
"msg_type": "Snapshot",
|
|
||||||
"order": 0,
|
|
||||||
"sender": 0,
|
|
||||||
"trace_id": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"devices": [],
|
|
||||||
"inventory_toolbox": [],
|
|
||||||
"links": [],
|
|
||||||
"message_id": 31,
|
|
||||||
"msg_type": "Snapshot",
|
|
||||||
"order": 1,
|
|
||||||
"sender": 0,
|
|
||||||
"trace_id": 2
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
The pre-test snapshot has order 0 and the post-test snapshot has order 1.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Network UI Test Models
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
![Models](designs/models.png)
|
|
||||||
|
|
||||||
* FSMTrace - The record of an event that happend during an FSM trace. This is used in the recording phase of test case data.
|
|
||||||
* EventTrace - The record of an event that happened during an event trace. This is used in the recording phase of test case data.
|
|
||||||
* Coverage - Per-line test case coverage as returned by istanbul JS code coverage tool.
|
|
||||||
* TopologySnapshot - A snapshot of the state of the network UI before or after the test was run.
|
|
||||||
* TestCase - The definition of a test case with a given name using the test case data schema defined above.
|
|
||||||
* Result - One of passed, failed, errored, skipped, aborted, not run, or blocked.
|
|
||||||
* TestResult - The record of one test case being run at a certain code location by a client at a certain time.
|
|
||||||
* CodeUnderTest - The record of what exactly was the code being tested at the time the test was run.
|
|
||||||
|
|
||||||
|
|
||||||
Messages
|
|
||||||
--------
|
|
||||||
|
|
||||||
JSON messages are passed over the `/network_ui/test` websocket between the test client and the test server.
|
|
||||||
The protocol that is used for all messages is in ABNF (RFC5234):
|
|
||||||
|
|
||||||
|
|
||||||
message_type = 'MultipleMessage' / 'MouseEvent' / 'MouseWheelEvent' / 'KeyEvent' / 'StartRecording' / 'StopRecording' / 'ViewPort' / 'FSMTrace' / 'ChannelTrace' / 'Snapshot' / 'EnableTest' / 'DisableTest' / 'StartTest' / 'TestCompleted' / 'TestResult' / 'Coverage'
|
|
||||||
message_data = '{' 'msg_type' ': ' message_type ', ' key-value *( ', ' key-value ) '}'
|
|
||||||
message = '[ id , ' posint ']' / '[ topology_id , ' posint ']' / '[' message_type ', ' message_data ']'
|
|
||||||
|
|
||||||
See https://github.com/AndyA/abnfgen/blob/master/andy/json.abnf for the rest of the JSON ABNF.
|
|
||||||
|
|
||||||
See [designs/messages.yml](designs/messages.yml) for the allowable keys and values for each message type.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Loading Tests
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Tests can be imported from exported data dumps from the `awx-manage datadump` command. From the Tower shell run
|
|
||||||
the following command:
|
|
||||||
|
|
||||||
awx-manage loaddata /awx_devel/network_ui_test_cases_2018_03_12.json
|
|
||||||
|
|
||||||
This will load the tests from the `network_ui_test_cases_2018_03_12.json` file.
|
|
||||||
|
|
||||||
Exporting Tests
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Use the standard Django dumpdata command to dump the test case data to a file:
|
|
||||||
|
|
||||||
awx-manage dumpdata network_ui_test.TestCase > /awx_devel/network_ui_test_cases_YYYY_MM_DD.json
|
|
||||||
|
|
||||||
|
|
||||||
Writing Tests Manually or Generating Tests
|
|
||||||
------------------------------------------
|
|
||||||
|
|
||||||
Use the empty test case schema above and add messages to the event_trace list. Then upload the
|
|
||||||
JSON test data to the upload test URL below.
|
|
||||||
|
|
||||||
Recording Tests
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Tests can be reruns of previously recorded manual interactions with the network UI. To start a recording
|
|
||||||
open the JavaScript console and run this command:
|
|
||||||
|
|
||||||
scope.onRecordButton()
|
|
||||||
|
|
||||||
To stop the recording run this command:
|
|
||||||
|
|
||||||
scope.onRecordButton()
|
|
||||||
|
|
||||||
To download the recording use this command:
|
|
||||||
|
|
||||||
scope.onDownloadRecordingButton()
|
|
||||||
|
|
||||||
|
|
||||||
After downloading the recording upload the data using the following URL to make a test of it.
|
|
||||||
|
|
||||||
Uploading Tests
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Go to the URL: https://SERVER:PORT/network_ui_test/upload_test and use the form to upload test data
|
|
||||||
and choose a test case name.
|
|
||||||
|
|
||||||
Instrumenting a UI Build
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
Code coverage is collected automatically if the UI is built with instrumented code. Add this section to the
|
|
||||||
rules in awx/ui/build/webpack.base.js to build instrumented code.
|
|
||||||
|
|
||||||
{
|
|
||||||
test: /\.js$/,
|
|
||||||
use: {
|
|
||||||
loader: 'istanbul-instrumenter-loader',
|
|
||||||
options: { esModules: true }
|
|
||||||
},
|
|
||||||
enforce: 'pre',
|
|
||||||
include: [
|
|
||||||
/src\/network-ui\//
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
Then run:
|
|
||||||
|
|
||||||
make ui-devel
|
|
||||||
|
|
||||||
or:
|
|
||||||
|
|
||||||
make ui-docker
|
|
||||||
|
|
||||||
|
|
||||||
To rebuild the code with istanbul instrumentation.
|
|
||||||
|
|
||||||
|
|
||||||
Running Tests
|
|
||||||
-------------
|
|
||||||
|
|
||||||
|
|
||||||
To kick off tests in the web browser navigate to the Network UI page under an inventory and open the JavaScript console.
|
|
||||||
Run this command in the JavaScript console:
|
|
||||||
|
|
||||||
scope.onRunTestsButton();
|
|
||||||
|
|
||||||
|
|
||||||
Building a Coverage Report
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
To build a coverage report for the last set of tests that were run as in above. Edit the `tools/Makefile` and change
|
|
||||||
`SERVER` to match your Tower server. Then run the following command from the `tools` directory:
|
|
||||||
|
|
||||||
make coverage
|
|
||||||
|
|
||||||
This will download all the coverage data from all the test results that were previously recorded and build a coverage
|
|
||||||
report with istantbul. You can then view the report at the address http://localhost:9000 or http://server:PORT
|
|
||||||
where you executed the command.
|
|
||||||
|
|
||||||
http://localhost:9000/coverage will contain the coverage for all test cases merged together.
|
|
||||||
http://localhost:9000/Load/coverage will contain the coverage for just loading the page.
|
|
||||||
http://localhost:9000/TestX/coverage will contain the coverage for just the TestX test where
|
|
||||||
TestX is the name of one of the tests run previously.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
3.3 Hardening Road Map
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
* Add post-test snapshot comparison
|
|
||||||
* Add FSM trace comparison
|
|
||||||
* Add an ability to run a single test
|
|
||||||
* Add an ability to run a subset of the tests
|
|
||||||
* Add a big red light when event recording is on.
|
|
||||||
|
|
||||||
|
|
@ -1 +0,0 @@
|
|||||||
# Copyright (c) 2017 Red Hat, Inc
|
|
@ -1,184 +0,0 @@
|
|||||||
# Copyright (c) 2018 Red Hat, Inc
|
|
||||||
# In consumers.py
|
|
||||||
from channels import Group, Channel
|
|
||||||
from channels.sessions import channel_session
|
|
||||||
from awx.network_ui.models import Topology, Client
|
|
||||||
from awx.network_ui.models import TopologyInventory
|
|
||||||
from awx.network_ui_test.models import FSMTrace, EventTrace, Coverage, TopologySnapshot
|
|
||||||
from awx.network_ui_test.models import TestCase, TestResult, CodeUnderTest, Result
|
|
||||||
import urlparse
|
|
||||||
import logging
|
|
||||||
from django.utils.dateparse import parse_datetime
|
|
||||||
|
|
||||||
|
|
||||||
import json
|
|
||||||
# Connected to websocket.connect
|
|
||||||
|
|
||||||
logger = logging.getLogger("awx.network_ui_test.consumers")
|
|
||||||
|
|
||||||
|
|
||||||
def parse_inventory_id(data):
|
|
||||||
inventory_id = data.get('inventory_id', ['null'])
|
|
||||||
try:
|
|
||||||
inventory_id = int(inventory_id[0])
|
|
||||||
except ValueError:
|
|
||||||
inventory_id = None
|
|
||||||
if not inventory_id:
|
|
||||||
inventory_id = None
|
|
||||||
return inventory_id
|
|
||||||
|
|
||||||
|
|
||||||
class TestPersistence(object):
|
|
||||||
|
|
||||||
def parse_message_text(self, message_text, client_id):
|
|
||||||
data = json.loads(message_text)
|
|
||||||
if len(data) == 2:
|
|
||||||
message_type = data.pop(0)
|
|
||||||
message_value = data.pop(0)
|
|
||||||
if isinstance(message_value, list):
|
|
||||||
logger.error("Message has no sender")
|
|
||||||
return None, None
|
|
||||||
if isinstance(message_value, dict) and client_id != message_value.get('sender'):
|
|
||||||
logger.error("client_id mismatch expected: %s actual %s", client_id, message_value.get('sender'))
|
|
||||||
return None, None
|
|
||||||
return message_type, message_value
|
|
||||||
else:
|
|
||||||
logger.error("Invalid message text")
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
def handle(self, message):
|
|
||||||
topology_id = message.get('topology')
|
|
||||||
assert topology_id is not None, "No topology_id"
|
|
||||||
client_id = message.get('client')
|
|
||||||
assert client_id is not None, "No client_id"
|
|
||||||
message_type, message_value = self.parse_message_text(message['text'], client_id)
|
|
||||||
if message_type is None:
|
|
||||||
return
|
|
||||||
handler = self.get_handler(message_type)
|
|
||||||
if handler is not None:
|
|
||||||
try:
|
|
||||||
handler(message_value, topology_id, client_id)
|
|
||||||
except Exception:
|
|
||||||
Group("client-%s" % client_id).send({"text": json.dumps(["Error", "Server Error"])})
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
logger.warning("Unsupported message %s: no handler", message_type)
|
|
||||||
|
|
||||||
def get_handler(self, message_type):
|
|
||||||
return getattr(self, "on{0}".format(message_type), None)
|
|
||||||
|
|
||||||
def onMultipleMessage(self, message_value, topology_id, client_id):
|
|
||||||
for message in message_value['messages']:
|
|
||||||
handler = self.get_handler(message['msg_type'])
|
|
||||||
if handler is not None:
|
|
||||||
handler(message, topology_id, client_id)
|
|
||||||
else:
|
|
||||||
logger.warning("Unsupported message %s", message['msg_type'])
|
|
||||||
|
|
||||||
def onCoverageRequest(self, coverage, topology_id, client_id):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def onTestResult(self, test_result, topology_id, client_id):
|
|
||||||
xyz, _, rest = test_result['code_under_test'].partition('-')
|
|
||||||
commits_since, _, commit_hash = rest.partition('-')
|
|
||||||
commit_hash = commit_hash.strip('g')
|
|
||||||
|
|
||||||
x, y, z = [int(i) for i in xyz.split('.')]
|
|
||||||
|
|
||||||
code_under_test, _ = CodeUnderTest.objects.get_or_create(version_x=x,
|
|
||||||
version_y=y,
|
|
||||||
version_z=z,
|
|
||||||
commits_since=int(commits_since),
|
|
||||||
commit_hash=commit_hash)
|
|
||||||
|
|
||||||
tr = TestResult(id=test_result['id'],
|
|
||||||
result_id=Result.objects.get(name=test_result['result']).pk,
|
|
||||||
test_case_id=TestCase.objects.get(name=test_result['name']).pk,
|
|
||||||
code_under_test_id=code_under_test.pk,
|
|
||||||
client_id=client_id,
|
|
||||||
time=parse_datetime(test_result['date']))
|
|
||||||
tr.save()
|
|
||||||
|
|
||||||
|
|
||||||
def onCoverage(self, coverage, topology_id, client_id):
|
|
||||||
Coverage(test_result_id=TestResult.objects.get(id=coverage['result_id'], client_id=client_id).pk,
|
|
||||||
coverage_data=json.dumps(coverage['coverage'])).save()
|
|
||||||
|
|
||||||
def onStartRecording(self, recording, topology_id, client_id):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def onStopRecording(self, recording, topology_id, client_id):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def write_event(self, event, topology_id, client_id):
|
|
||||||
if event.get('save', True):
|
|
||||||
EventTrace(trace_session_id=event['trace_id'],
|
|
||||||
event_data=json.dumps(event),
|
|
||||||
message_id=event['message_id'],
|
|
||||||
client_id=client_id).save()
|
|
||||||
|
|
||||||
onViewPort = write_event
|
|
||||||
onMouseEvent = write_event
|
|
||||||
onTouchEvent = write_event
|
|
||||||
onMouseWheelEvent = write_event
|
|
||||||
onKeyEvent = write_event
|
|
||||||
|
|
||||||
def onFSMTrace(self, message_value, diagram_id, client_id):
|
|
||||||
FSMTrace(trace_session_id=message_value['trace_id'],
|
|
||||||
fsm_name=message_value['fsm_name'],
|
|
||||||
from_state=message_value['from_state'],
|
|
||||||
to_state=message_value['to_state'],
|
|
||||||
order=message_value['order'],
|
|
||||||
client_id=client_id,
|
|
||||||
message_type=message_value['recv_message_type'] or "none").save()
|
|
||||||
|
|
||||||
def onSnapshot(self, snapshot, topology_id, client_id):
|
|
||||||
TopologySnapshot(trace_session_id=snapshot['trace_id'],
|
|
||||||
snapshot_data=json.dumps(snapshot),
|
|
||||||
order=snapshot['order'],
|
|
||||||
client_id=client_id,
|
|
||||||
topology_id=topology_id).save()
|
|
||||||
|
|
||||||
|
|
||||||
@channel_session
|
|
||||||
def ws_connect(message):
|
|
||||||
# Accept connection
|
|
||||||
data = urlparse.parse_qs(message.content['query_string'])
|
|
||||||
inventory_id = parse_inventory_id(data)
|
|
||||||
topology_ids = list(TopologyInventory.objects.filter(inventory_id=inventory_id).values_list('topology_id', flat=True))
|
|
||||||
topology_id = None
|
|
||||||
if len(topology_ids) > 0:
|
|
||||||
topology_id = topology_ids[0]
|
|
||||||
if topology_id is not None:
|
|
||||||
topology = Topology.objects.get(topology_id=topology_id)
|
|
||||||
else:
|
|
||||||
topology = Topology(name="topology", scale=1.0, panX=0, panY=0)
|
|
||||||
topology.save()
|
|
||||||
TopologyInventory(inventory_id=inventory_id, topology_id=topology.topology_id).save()
|
|
||||||
topology_id = topology.topology_id
|
|
||||||
message.channel_session['topology_id'] = topology_id
|
|
||||||
client = Client()
|
|
||||||
client.save()
|
|
||||||
message.channel_session['client_id'] = client.pk
|
|
||||||
Group("client-%s" % client.pk).add(message.reply_channel)
|
|
||||||
message.reply_channel.send({"text": json.dumps(["id", client.pk])})
|
|
||||||
send_tests(message.reply_channel)
|
|
||||||
|
|
||||||
|
|
||||||
def send_tests(channel):
|
|
||||||
for name, test_case_data in TestCase.objects.all().values_list('name', 'test_case_data'):
|
|
||||||
channel.send({"text": json.dumps(["TestCase", [name, json.loads(test_case_data)]])})
|
|
||||||
|
|
||||||
|
|
||||||
@channel_session
|
|
||||||
def ws_message(message):
|
|
||||||
Channel('test_persistence').send({"text": message['text'],
|
|
||||||
"topology": message.channel_session['topology_id'],
|
|
||||||
"client": message.channel_session['client_id']})
|
|
||||||
|
|
||||||
|
|
||||||
@channel_session
|
|
||||||
def ws_disconnect(message):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
|||||||
messages:
|
|
||||||
- {msg_type: MultipleMessage, fields: [msg_type, sender, messages]}
|
|
||||||
- {msg_type: MouseEvent, fields: [msg_type, sender, x, y, type, trace_id]}
|
|
||||||
- {msg_type: MouseWheelEvent, fields: [msg_type, sender, delta, deltaX, deltaY, type, originalEvent, trace_id]}
|
|
||||||
- {msg_type: KeyEvent, fields: [msg_type, sender, key, keyCode, type, altKey, shiftKey, ctrlKey, metaKey, trace_id]}
|
|
||||||
- {msg_type: StartRecording, fields: [msg_type, sender, trace_id]}
|
|
||||||
- {msg_type: StopRecording, fields: [msg_type, sender, trace_id]}
|
|
||||||
- {msg_type: ViewPort, fields: [msg_type, sender, scale, panX, panY, trace_id]}
|
|
||||||
- {msg_type: FSMTrace, fields: [msg_type, order, sender, trace_id, fsm_name, from_state, to_state, recv_message_type]}
|
|
||||||
- {msg_type: ChannelTrace, fields: [msg_type, sender, trace_id, from_fsm, to_fsm, sent_message_type]}
|
|
||||||
- {msg_type: Snapshot, fields: [msg_type, sender, devices, links, groups, streams, order, trace_id]}
|
|
||||||
- {msg_type: EnableTest, fields: [msg_type]}
|
|
||||||
- {msg_type: DisableTest, fields: [msg_type]}
|
|
||||||
- {msg_type: StartTest, fields: [msg_type]}
|
|
||||||
- {msg_type: TestCompleted, fields: [msg_type]}
|
|
||||||
- {msg_type: TestResult, fields: [msg_type, sender, id, name, result, date, code_under_test]}
|
|
||||||
- {msg_type: Coverage, fields: [msg_type, sender, coverage, result_id]}
|
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 156 KiB |
@ -1,168 +0,0 @@
|
|||||||
app: awx.network_ui_test
|
|
||||||
external_models: []
|
|
||||||
models:
|
|
||||||
- fields:
|
|
||||||
- name: client_id
|
|
||||||
pk: true
|
|
||||||
type: AutoField
|
|
||||||
name: Client
|
|
||||||
x: -518
|
|
||||||
y: 138
|
|
||||||
- fields:
|
|
||||||
- name: fsm_trace_id
|
|
||||||
pk: true
|
|
||||||
type: AutoField
|
|
||||||
- len: 200
|
|
||||||
name: fsm_name
|
|
||||||
type: CharField
|
|
||||||
- len: 200
|
|
||||||
name: from_state
|
|
||||||
type: CharField
|
|
||||||
- len: 200
|
|
||||||
name: to_state
|
|
||||||
type: CharField
|
|
||||||
- len: 200
|
|
||||||
name: message_type
|
|
||||||
type: CharField
|
|
||||||
- name: client
|
|
||||||
ref: Client
|
|
||||||
ref_field: client_id
|
|
||||||
type: ForeignKey
|
|
||||||
- default: 0
|
|
||||||
name: trace_session_id
|
|
||||||
type: IntegerField
|
|
||||||
- default: 0
|
|
||||||
name: order
|
|
||||||
type: IntegerField
|
|
||||||
name: FSMTrace
|
|
||||||
x: -872
|
|
||||||
y: 507
|
|
||||||
- fields:
|
|
||||||
- name: event_trace_id
|
|
||||||
pk: true
|
|
||||||
type: AutoField
|
|
||||||
- name: client
|
|
||||||
ref: Client
|
|
||||||
ref_field: client_id
|
|
||||||
type: ForeignKey
|
|
||||||
- default: 0
|
|
||||||
name: trace_session_id
|
|
||||||
type: IntegerField
|
|
||||||
- name: event_data
|
|
||||||
type: TextField
|
|
||||||
- name: message_id
|
|
||||||
type: IntegerField
|
|
||||||
name: EventTrace
|
|
||||||
x: -1087
|
|
||||||
y: 202
|
|
||||||
- fields:
|
|
||||||
- name: coverage_id
|
|
||||||
pk: true
|
|
||||||
type: AutoField
|
|
||||||
- name: coverage_data
|
|
||||||
type: TextField
|
|
||||||
- name: test_result
|
|
||||||
ref: TestResult
|
|
||||||
ref_field: test_result_id
|
|
||||||
type: ForeignKey
|
|
||||||
name: Coverage
|
|
||||||
x: -1068
|
|
||||||
y: -4
|
|
||||||
- fields:
|
|
||||||
- name: topology_snapshot_id
|
|
||||||
pk: true
|
|
||||||
type: AutoField
|
|
||||||
- name: client
|
|
||||||
ref: Client
|
|
||||||
ref_field: client_id
|
|
||||||
type: ForeignKey
|
|
||||||
- name: topology_id
|
|
||||||
type: IntegerField
|
|
||||||
- name: trace_session_id
|
|
||||||
type: IntegerField
|
|
||||||
- name: snapshot_data
|
|
||||||
ref: TopologySnapshot
|
|
||||||
ref_field: snapshot_data
|
|
||||||
type: TextField
|
|
||||||
- name: order
|
|
||||||
type: IntegerField
|
|
||||||
name: TopologySnapshot
|
|
||||||
x: -1123
|
|
||||||
y: -277
|
|
||||||
- fields:
|
|
||||||
- name: test_case_id
|
|
||||||
pk: true
|
|
||||||
type: AutoField
|
|
||||||
- len: 200
|
|
||||||
name: name
|
|
||||||
ref: TestCase
|
|
||||||
ref_field: name
|
|
||||||
type: CharField
|
|
||||||
- name: test_case_data
|
|
||||||
type: TextField
|
|
||||||
name: TestCase
|
|
||||||
x: -1642
|
|
||||||
y: -38
|
|
||||||
- fields:
|
|
||||||
- name: result_id
|
|
||||||
pk: true
|
|
||||||
type: AutoField
|
|
||||||
- len: 20
|
|
||||||
name: name
|
|
||||||
type: CharField
|
|
||||||
name: Result
|
|
||||||
x: -1610
|
|
||||||
y: 120
|
|
||||||
- fields:
|
|
||||||
- name: code_under_test_id
|
|
||||||
pk: true
|
|
||||||
ref: CodeUnderTest
|
|
||||||
ref_field: code_under_test_id
|
|
||||||
type: AutoField
|
|
||||||
- name: version_x
|
|
||||||
type: IntegerField
|
|
||||||
- name: version_y
|
|
||||||
type: IntegerField
|
|
||||||
- name: version_z
|
|
||||||
type: IntegerField
|
|
||||||
- name: commits_since
|
|
||||||
type: IntegerField
|
|
||||||
- len: 40
|
|
||||||
name: commit_hash
|
|
||||||
type: CharField
|
|
||||||
name: CodeUnderTest
|
|
||||||
x: -1612
|
|
||||||
y: 259
|
|
||||||
- fields:
|
|
||||||
- name: test_result_id
|
|
||||||
pk: true
|
|
||||||
type: AutoField
|
|
||||||
- name: test_case
|
|
||||||
ref: TestCase
|
|
||||||
ref_field: test_case_id
|
|
||||||
type: ForeignKey
|
|
||||||
- name: result
|
|
||||||
ref: Result
|
|
||||||
ref_field: result_id
|
|
||||||
type: ForeignKey
|
|
||||||
- name: code_under_test
|
|
||||||
ref: CodeUnderTest
|
|
||||||
ref_field: code_under_test_id
|
|
||||||
type: ForeignKey
|
|
||||||
- name: time
|
|
||||||
type: DateTimeField
|
|
||||||
- default: 0
|
|
||||||
name: id
|
|
||||||
type: IntegerField
|
|
||||||
- name: client
|
|
||||||
ref: Client
|
|
||||||
ref_field: client_id
|
|
||||||
type: ForeignKey
|
|
||||||
name: TestResult
|
|
||||||
x: -1336
|
|
||||||
y: -49
|
|
||||||
modules: []
|
|
||||||
view:
|
|
||||||
panX: 213.729555519212
|
|
||||||
panY: 189.446959094643
|
|
||||||
scaleXY: 0.69
|
|
@ -1,102 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11.7 on 2018-03-06 18:40
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('network_ui', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='CodeUnderTest',
|
|
||||||
fields=[
|
|
||||||
('code_under_test_id', models.AutoField(primary_key=True, serialize=False, verbose_name=b'CodeUnderTest')),
|
|
||||||
('version_x', models.IntegerField()),
|
|
||||||
('version_y', models.IntegerField()),
|
|
||||||
('version_z', models.IntegerField()),
|
|
||||||
('commits_since', models.IntegerField()),
|
|
||||||
('commit_hash', models.CharField(blank=True, max_length=40)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Coverage',
|
|
||||||
fields=[
|
|
||||||
('coverage_id', models.AutoField(primary_key=True, serialize=False)),
|
|
||||||
('coverage_data', models.TextField()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='EventTrace',
|
|
||||||
fields=[
|
|
||||||
('event_trace_id', models.AutoField(primary_key=True, serialize=False)),
|
|
||||||
('trace_session_id', models.IntegerField(default=0)),
|
|
||||||
('event_data', models.TextField()),
|
|
||||||
('message_id', models.IntegerField()),
|
|
||||||
('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui.Client')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='FSMTrace',
|
|
||||||
fields=[
|
|
||||||
('fsm_trace_id', models.AutoField(primary_key=True, serialize=False)),
|
|
||||||
('fsm_name', models.CharField(blank=True, max_length=200)),
|
|
||||||
('from_state', models.CharField(blank=True, max_length=200)),
|
|
||||||
('to_state', models.CharField(blank=True, max_length=200)),
|
|
||||||
('message_type', models.CharField(blank=True, max_length=200)),
|
|
||||||
('trace_session_id', models.IntegerField(default=0)),
|
|
||||||
('order', models.IntegerField(default=0)),
|
|
||||||
('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui.Client')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Result',
|
|
||||||
fields=[
|
|
||||||
('result_id', models.AutoField(primary_key=True, serialize=False)),
|
|
||||||
('name', models.CharField(blank=True, max_length=20)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='TestCase',
|
|
||||||
fields=[
|
|
||||||
('test_case_id', models.AutoField(primary_key=True, serialize=False)),
|
|
||||||
('name', models.CharField(blank=True, max_length=200, verbose_name=b'TestCase')),
|
|
||||||
('test_case_data', models.TextField()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='TestResult',
|
|
||||||
fields=[
|
|
||||||
('test_result_id', models.AutoField(primary_key=True, serialize=False)),
|
|
||||||
('time', models.DateTimeField()),
|
|
||||||
('id', models.IntegerField(default=0)),
|
|
||||||
('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui.Client')),
|
|
||||||
('code_under_test', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui_test.CodeUnderTest')),
|
|
||||||
('result', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui_test.Result')),
|
|
||||||
('test_case', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui_test.TestCase')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='TopologySnapshot',
|
|
||||||
fields=[
|
|
||||||
('topology_snapshot_id', models.AutoField(primary_key=True, serialize=False)),
|
|
||||||
('topology_id', models.IntegerField()),
|
|
||||||
('trace_session_id', models.IntegerField()),
|
|
||||||
('snapshot_data', models.TextField(verbose_name=b'TopologySnapshot')),
|
|
||||||
('order', models.IntegerField()),
|
|
||||||
('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui.Client')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='coverage',
|
|
||||||
name='test_result',
|
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='network_ui_test.TestResult'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,29 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11.7 on 2018-03-06 22:43
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
results = ['passed', 'failed', 'errored', 'skipped',
|
|
||||||
'aborted', 'not run', 'blocked']
|
|
||||||
|
|
||||||
|
|
||||||
def populate_result_types(apps, schema_editor):
|
|
||||||
|
|
||||||
Result = apps.get_model('network_ui_test', 'Result')
|
|
||||||
for result in results:
|
|
||||||
Result.objects.get_or_create(name=result)
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('network_ui_test', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RunPython(
|
|
||||||
code=populate_result_types,
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,26 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11.7 on 2018-03-06 23:06
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
def add_load_test_case(apps, schema_editor):
|
|
||||||
|
|
||||||
TestCase = apps.get_model('network_ui_test', 'TestCase')
|
|
||||||
TestCase.objects.get_or_create(name="Load", test_case_data=json.dumps(dict(runnable=False)))
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('network_ui_test', '0002_auto_20180306_2243'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RunPython(
|
|
||||||
code=add_load_test_case,
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,73 +0,0 @@
|
|||||||
from django.db import models
|
|
||||||
|
|
||||||
|
|
||||||
class FSMTrace(models.Model):
|
|
||||||
|
|
||||||
fsm_trace_id = models.AutoField(primary_key=True,)
|
|
||||||
fsm_name = models.CharField(max_length=200, blank=True)
|
|
||||||
from_state = models.CharField(max_length=200, blank=True)
|
|
||||||
to_state = models.CharField(max_length=200, blank=True)
|
|
||||||
message_type = models.CharField(max_length=200, blank=True)
|
|
||||||
client = models.ForeignKey('network_ui.Client',)
|
|
||||||
trace_session_id = models.IntegerField(default=0,)
|
|
||||||
order = models.IntegerField(default=0,)
|
|
||||||
|
|
||||||
|
|
||||||
class EventTrace(models.Model):
|
|
||||||
|
|
||||||
event_trace_id = models.AutoField(primary_key=True,)
|
|
||||||
client = models.ForeignKey('network_ui.Client',)
|
|
||||||
trace_session_id = models.IntegerField(default=0,)
|
|
||||||
event_data = models.TextField()
|
|
||||||
message_id = models.IntegerField()
|
|
||||||
|
|
||||||
|
|
||||||
class Coverage(models.Model):
|
|
||||||
|
|
||||||
coverage_id = models.AutoField(primary_key=True,)
|
|
||||||
coverage_data = models.TextField()
|
|
||||||
test_result = models.ForeignKey('TestResult',)
|
|
||||||
|
|
||||||
|
|
||||||
class TopologySnapshot(models.Model):
|
|
||||||
|
|
||||||
topology_snapshot_id = models.AutoField(primary_key=True,)
|
|
||||||
client = models.ForeignKey('network_ui.Client',)
|
|
||||||
topology_id = models.IntegerField()
|
|
||||||
trace_session_id = models.IntegerField()
|
|
||||||
snapshot_data = models.TextField('TopologySnapshot',)
|
|
||||||
order = models.IntegerField()
|
|
||||||
|
|
||||||
|
|
||||||
class TestCase(models.Model):
|
|
||||||
|
|
||||||
test_case_id = models.AutoField(primary_key=True,)
|
|
||||||
name = models.CharField('TestCase', max_length=200, blank=True)
|
|
||||||
test_case_data = models.TextField()
|
|
||||||
|
|
||||||
|
|
||||||
class Result(models.Model):
|
|
||||||
|
|
||||||
result_id = models.AutoField(primary_key=True,)
|
|
||||||
name = models.CharField(max_length=20, blank=True)
|
|
||||||
|
|
||||||
|
|
||||||
class CodeUnderTest(models.Model):
|
|
||||||
|
|
||||||
code_under_test_id = models.AutoField('CodeUnderTest', primary_key=True,)
|
|
||||||
version_x = models.IntegerField()
|
|
||||||
version_y = models.IntegerField()
|
|
||||||
version_z = models.IntegerField()
|
|
||||||
commits_since = models.IntegerField()
|
|
||||||
commit_hash = models.CharField(max_length=40, blank=True)
|
|
||||||
|
|
||||||
|
|
||||||
class TestResult(models.Model):
|
|
||||||
|
|
||||||
test_result_id = models.AutoField(primary_key=True,)
|
|
||||||
test_case = models.ForeignKey('TestCase',)
|
|
||||||
result = models.ForeignKey('Result',)
|
|
||||||
code_under_test = models.ForeignKey('CodeUnderTest',)
|
|
||||||
time = models.DateTimeField()
|
|
||||||
id = models.IntegerField(default=0,)
|
|
||||||
client = models.ForeignKey('network_ui.Client',)
|
|
@ -1,12 +0,0 @@
|
|||||||
# Copyright (c) 2017 Red Hat, Inc
|
|
||||||
from channels.routing import route
|
|
||||||
from awx.network_ui_test.consumers import ws_connect, ws_message, ws_disconnect, TestPersistence
|
|
||||||
|
|
||||||
|
|
||||||
channel_routing = [
|
|
||||||
route("websocket.connect", ws_connect, path=r"^/network_ui/test"),
|
|
||||||
route("websocket.receive", ws_message, path=r"^/network_ui/test"),
|
|
||||||
route("websocket.disconnect", ws_disconnect, path=r"^/network_ui/test"),
|
|
||||||
route("test_persistence", TestPersistence().handle),
|
|
||||||
]
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
|||||||
<form action="/network_ui_test/upload_test" method="post" enctype="multipart/form-data">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{form}}
|
|
||||||
<button type="submit">Upload Test</button>
|
|
||||||
</form>
|
|
@ -1,13 +0,0 @@
|
|||||||
|
|
||||||
SERVER = "https://meganuke:8043"
|
|
||||||
PORT = "9000"
|
|
||||||
|
|
||||||
.PHONY: clean coverage
|
|
||||||
|
|
||||||
clean:
|
|
||||||
git clean -fdX .
|
|
||||||
git clean -fd .
|
|
||||||
|
|
||||||
coverage:
|
|
||||||
./coverage_report.py ${SERVER}
|
|
||||||
python -m SimpleHTTPServer ${PORT}
|
|
@ -1,51 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
Usage:
|
|
||||||
coverage_report [options] <server>
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h, --help Show this page
|
|
||||||
--debug Show debug logging
|
|
||||||
--verbose Show verbose logging
|
|
||||||
"""
|
|
||||||
from docopt import docopt
|
|
||||||
import logging
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import requests
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
logger = logging.getLogger('coverage_report')
|
|
||||||
|
|
||||||
TESTS_API = '/network_ui_test/tests'
|
|
||||||
|
|
||||||
|
|
||||||
def main(args=None):
|
|
||||||
if args is None:
|
|
||||||
args = sys.argv[1:]
|
|
||||||
parsed_args = docopt(__doc__, args)
|
|
||||||
if parsed_args['--debug']:
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
|
||||||
elif parsed_args['--verbose']:
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
|
||||||
else:
|
|
||||||
logging.basicConfig(level=logging.WARNING)
|
|
||||||
|
|
||||||
server = parsed_args['<server>']
|
|
||||||
tests = requests.get(server + TESTS_API, verify=False).json()
|
|
||||||
|
|
||||||
for test in tests['tests']:
|
|
||||||
if not os.path.exists(test['name']):
|
|
||||||
os.mkdir(test['name'])
|
|
||||||
with open(test['name'] + "/coverage.json", 'w') as f:
|
|
||||||
f.write(requests.get(server + test['coverage'], verify=False).text)
|
|
||||||
|
|
||||||
for test in tests['tests']:
|
|
||||||
subprocess.Popen('istanbul report html', shell=True, cwd=test['name']).wait()
|
|
||||||
subprocess.Popen('istanbul report html', shell=True).wait()
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
sys.exit(main(sys.argv[1:]))
|
|
@ -1,2 +0,0 @@
|
|||||||
requests
|
|
||||||
docopt
|
|
@ -1,14 +0,0 @@
|
|||||||
# Copyright (c) 2017 Red Hat, Inc
|
|
||||||
from django.conf.urls import url
|
|
||||||
|
|
||||||
from awx.network_ui_test import views
|
|
||||||
|
|
||||||
app_name = 'network_ui_test'
|
|
||||||
urlpatterns = [
|
|
||||||
url(r'^tests$', views.tests, name='tests'),
|
|
||||||
url(r'^upload_test$', views.upload_test, name='upload_test'),
|
|
||||||
url(r'^download_coverage/(?P<pk>[0-9]+)$', views.download_coverage, name='download_coverage'),
|
|
||||||
url(r'^download_trace$', views.download_trace, name='download_trace'),
|
|
||||||
url(r'^download_recording$', views.download_recording, name='download_recording'),
|
|
||||||
]
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
|||||||
# Copyright (c) 2017 Red Hat, Inc
|
|
||||||
from django.shortcuts import render
|
|
||||||
from django import forms
|
|
||||||
from django.http import JsonResponse, HttpResponse, HttpResponseRedirect
|
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
# Create your views here.
|
|
||||||
from .models import FSMTrace, EventTrace, TopologySnapshot
|
|
||||||
from .models import TestCase, TestResult, Coverage
|
|
||||||
|
|
||||||
|
|
||||||
class FSMTraceForm(forms.Form):
|
|
||||||
topology_id = forms.IntegerField()
|
|
||||||
trace_id = forms.IntegerField()
|
|
||||||
client_id = forms.IntegerField()
|
|
||||||
|
|
||||||
|
|
||||||
def download_trace(request):
|
|
||||||
form = FSMTraceForm(request.GET)
|
|
||||||
if form.is_valid():
|
|
||||||
topology_id = form.cleaned_data['topology_id']
|
|
||||||
trace_id = form.cleaned_data['trace_id']
|
|
||||||
client_id = form.cleaned_data['client_id']
|
|
||||||
data = list(FSMTrace.objects.filter(trace_session_id=trace_id,
|
|
||||||
client_id=client_id).order_by('order').values())
|
|
||||||
response = HttpResponse(yaml.safe_dump(data, default_flow_style=False),
|
|
||||||
content_type="application/force-download")
|
|
||||||
response['Content-Disposition'] = 'attachment; filename="trace_{0}_{1}_{2}.yml"'.format(topology_id, client_id, trace_id)
|
|
||||||
return response
|
|
||||||
else:
|
|
||||||
return HttpResponse(form.errors)
|
|
||||||
|
|
||||||
|
|
||||||
class RecordingForm(forms.Form):
|
|
||||||
topology_id = forms.IntegerField()
|
|
||||||
trace_id = forms.IntegerField()
|
|
||||||
client_id = forms.IntegerField()
|
|
||||||
|
|
||||||
|
|
||||||
def download_recording(request):
|
|
||||||
form = RecordingForm(request.GET)
|
|
||||||
if form.is_valid():
|
|
||||||
topology_id = form.cleaned_data['topology_id']
|
|
||||||
trace_id = form.cleaned_data['trace_id']
|
|
||||||
client_id = form.cleaned_data['client_id']
|
|
||||||
data = dict()
|
|
||||||
data['event_trace'] = [json.loads(x) for x in EventTrace
|
|
||||||
.objects.filter(trace_session_id=trace_id, client_id=client_id)
|
|
||||||
.order_by('message_id')
|
|
||||||
.values_list('event_data', flat=True)]
|
|
||||||
data['fsm_trace'] = list(FSMTrace
|
|
||||||
.objects
|
|
||||||
.filter(trace_session_id=trace_id, client_id=client_id)
|
|
||||||
.order_by('order')
|
|
||||||
.values())
|
|
||||||
data['snapshots'] = [json.loads(x) for x in TopologySnapshot
|
|
||||||
.objects.filter(trace_session_id=trace_id, client_id=client_id)
|
|
||||||
.order_by('order')
|
|
||||||
.values_list('snapshot_data', flat=True)]
|
|
||||||
response = HttpResponse(json.dumps(data, sort_keys=True, indent=4),
|
|
||||||
content_type="application/force-download")
|
|
||||||
response['Content-Disposition'] = 'attachment; filename="trace_{0}_{1}_{2}.yml"'.format(topology_id, client_id, trace_id)
|
|
||||||
return response
|
|
||||||
else:
|
|
||||||
return HttpResponse(form.errors)
|
|
||||||
|
|
||||||
|
|
||||||
def tests(request):
|
|
||||||
tests = list(TestCase.objects.all().values('test_case_id', 'name'))
|
|
||||||
for x in tests:
|
|
||||||
x['coverage'] = "/network_ui_test/download_coverage/{0}".format(x['test_case_id'])
|
|
||||||
return JsonResponse(dict(tests=tests))
|
|
||||||
|
|
||||||
|
|
||||||
def create_test(name, data):
|
|
||||||
try:
|
|
||||||
test_case = TestCase.objects.get(name=name)
|
|
||||||
test_case.test_case_data=json.dumps(data)
|
|
||||||
test_case.save()
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
TestCase(name=name, test_case_data=json.dumps(data)).save()
|
|
||||||
|
|
||||||
|
|
||||||
class UploadTestForm(forms.Form):
|
|
||||||
name = forms.CharField()
|
|
||||||
file = forms.FileField()
|
|
||||||
|
|
||||||
|
|
||||||
def upload_test(request):
|
|
||||||
if request.method == 'POST':
|
|
||||||
form = UploadTestForm(request.POST, request.FILES)
|
|
||||||
if form.is_valid():
|
|
||||||
name = form.cleaned_data['name']
|
|
||||||
data = json.loads(request.FILES['file'].read())
|
|
||||||
create_test(name, data)
|
|
||||||
return HttpResponseRedirect('/network_ui_test/tests')
|
|
||||||
else:
|
|
||||||
form = UploadTestForm()
|
|
||||||
return render(request, 'network_ui_test/upload_test.html', {'form': form})
|
|
||||||
|
|
||||||
|
|
||||||
def download_coverage(request, pk):
|
|
||||||
latest_tr = TestResult.objects.filter(test_case_id=pk).order_by('-time')[0]
|
|
||||||
coverage = Coverage.objects.get(test_result_id=latest_tr.pk)
|
|
||||||
response = HttpResponse(coverage.coverage_data,
|
|
||||||
content_type="application/json")
|
|
||||||
return response
|
|
||||||
|
|
@ -279,8 +279,7 @@ INSTALLED_APPS = (
|
|||||||
'awx.ui',
|
'awx.ui',
|
||||||
'awx.sso',
|
'awx.sso',
|
||||||
'solo',
|
'solo',
|
||||||
'awx.network_ui',
|
'awx.network_ui'
|
||||||
'awx.network_ui_test',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
INTERNAL_IPS = ('127.0.0.1',)
|
INTERNAL_IPS = ('127.0.0.1',)
|
||||||
|
@ -35,6 +35,7 @@ var NetworkUIController = function($scope,
|
|||||||
|
|
||||||
$scope.api_token = '';
|
$scope.api_token = '';
|
||||||
$scope.disconnected = false;
|
$scope.disconnected = false;
|
||||||
|
$scope.tests_enabled = false;
|
||||||
|
|
||||||
$scope.topology_id = 0;
|
$scope.topology_id = 0;
|
||||||
// Create a web socket to connect to the backend server
|
// Create a web socket to connect to the backend server
|
||||||
@ -54,9 +55,17 @@ var NetworkUIController = function($scope,
|
|||||||
$scope.control_socket = new ReconnectingWebSocket(protocol + "://" + window.location.host + "/network_ui/topology?inventory_id=" + $scope.inventory_id,
|
$scope.control_socket = new ReconnectingWebSocket(protocol + "://" + window.location.host + "/network_ui/topology?inventory_id=" + $scope.inventory_id,
|
||||||
null,
|
null,
|
||||||
{debug: false, reconnectInterval: 300});
|
{debug: false, reconnectInterval: 300});
|
||||||
$scope.test_socket = new ReconnectingWebSocket(protocol + "://" + window.location.host + "/network_ui/test?inventory_id=" + $scope.inventory_id,
|
if ($scope.tests_enabled) {
|
||||||
null,
|
$scope.test_socket = new ReconnectingWebSocket(protocol + "://" + window.location.host + "/network_ui/test?inventory_id=" + $scope.inventory_id,
|
||||||
{debug: false, reconnectInterval: 300});
|
null,
|
||||||
|
{debug: false, reconnectInterval: 300});
|
||||||
|
} else {
|
||||||
|
$scope.test_socket = {
|
||||||
|
on_message: util.noop,
|
||||||
|
send: util.noop
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$scope.control_socket = {
|
$scope.control_socket = {
|
||||||
on_message: util.noop
|
on_message: util.noop
|
||||||
|
@ -16,7 +16,6 @@ urlpatterns = [
|
|||||||
url(r'^sso/', include('awx.sso.urls', namespace='sso')),
|
url(r'^sso/', include('awx.sso.urls', namespace='sso')),
|
||||||
url(r'^sso/', include('social_django.urls', namespace='social')),
|
url(r'^sso/', include('social_django.urls', namespace='social')),
|
||||||
url(r'^network_ui/', include('awx.network_ui.urls', namespace='network_uiui', app_name='network_ui')),
|
url(r'^network_ui/', include('awx.network_ui.urls', namespace='network_uiui', app_name='network_ui')),
|
||||||
url(r'^network_ui_test/', include('awx.network_ui_test.urls', namespace='network_ui_test', app_name='network_ui_test')),
|
|
||||||
url(r'^(?:api/)?400.html$', handle_400),
|
url(r'^(?:api/)?400.html$', handle_400),
|
||||||
url(r'^(?:api/)?403.html$', handle_403),
|
url(r'^(?:api/)?403.html$', handle_403),
|
||||||
url(r'^(?:api/)?404.html$', handle_404),
|
url(r'^(?:api/)?404.html$', handle_404),
|
||||||
|
Loading…
Reference in New Issue
Block a user