1+ /*
2+ * Copyright 2018 Philipp Salvisberg <philipp.salvisberg@trivadis.com>
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+ package org.utplsql.sqldev.dal
17+
18+ import java.io.StringReader
19+ import java.sql.Connection
20+ import java.sql.ResultSet
21+ import java.sql.SQLException
22+ import java.util.List
23+ import java.util.logging.Logger
24+ import javax.xml.parsers.DocumentBuilderFactory
25+ import org.springframework.dao.DataAccessException
26+ import org.springframework.jdbc.core.JdbcTemplate
27+ import org.springframework.jdbc.core.ResultSetExtractor
28+ import org.springframework.jdbc.datasource.SingleConnectionDataSource
29+ import org.utplsql.sqldev.model.XMLTools
30+ import org.utplsql.sqldev.model.runner.Counter
31+ import org.utplsql.sqldev.model.runner.Expectation
32+ import org.utplsql.sqldev.model.runner.PostEvent
33+ import org.utplsql.sqldev.model.runner.PostRunEvent
34+ import org.utplsql.sqldev.model.runner.PostSuiteEvent
35+ import org.utplsql.sqldev.model.runner.PostTestEvent
36+ import org.utplsql.sqldev.model.runner.PreRunEvent
37+ import org.utplsql.sqldev.model.runner.PreSuiteEvent
38+ import org.utplsql.sqldev.model.runner.PreTestEvent
39+ import org.utplsql.sqldev.model.runner.RealtimeReporterEvent
40+ import org.utplsql.sqldev.model.runner.Suite
41+ import org.utplsql.sqldev.model.runner.Test
42+ import org.w3c.dom.Document
43+ import org.w3c.dom.Element
44+ import org.w3c.dom.Node
45+ import org.xml.sax.InputSource
46+
47+ class RealtimeReporterDao {
48+ static val Logger logger = Logger . getLogger(RealtimeReporterDao . name);
49+ static val FIRST_VERSION_WITH_REALTIME_REPORTER = 3001004
50+ val extension XMLTools xmlTools = new XMLTools
51+ var Connection conn
52+ var JdbcTemplate jdbcTemplate
53+
54+ new (Connection connection) {
55+ conn = connection
56+ jdbcTemplate = new JdbcTemplate (new SingleConnectionDataSource (conn, true ))
57+ jdbcTemplate. fetchSize = 1
58+ if ((new UtplsqlDao (conn)). normalizedUtPlsqlVersionNumber < FIRST_VERSION_WITH_REALTIME_REPORTER ) {
59+ throw new RuntimeException (" RealtimeReporter requires utPLSQL v3.1.4 or higher to be installed." )
60+ }
61+ }
62+
63+ def produceReport (String reporterId , List<String > pathList ) {
64+ var plsql = ' ' '
65+ DECLARE
66+ l_reporter ut_realtime_reporter := ut_realtime_reporter();
67+ BEGIN
68+ l_reporter.set_reporter_id(?);
69+ l_reporter.output_buffer.init();
70+ sys.dbms_output.enable(NULL);
71+ ut_runner.run(
72+ a_paths => ut_varchar2_list(
73+ «FOR path : pathList SEPARATOR ","»
74+ ' «path»'
75+ «ENDFOR»
76+ ),
77+ a_reporters => ut_reporters(l_reporter)
78+ );
79+ sys.dbms_output.disable;
80+ END;
81+ ' ' '
82+ jdbcTemplate. update(plsql, #[reporterId])
83+ }
84+
85+ def consumeReport (String reporterId , RealtimeReporterEventConsumer consumer ) {
86+ var sql = ' ' '
87+ SELECT t.item_type, t.text
88+ FROM table(ut_output_table_buffer(?).get_lines()) t
89+ ' ' '
90+ jdbcTemplate. query(sql, new ResultSetExtractor<Void > () {
91+ override extractData(ResultSet rs) throws SQLException , DataAccessException {
92+ while (rs. next) {
93+ val itemType = rs. getString(" item_type" )
94+ val textClob = rs. getClob(" text" )
95+ val textString = textClob. getSubString(1 , textClob. length as int )
96+ val event = convert(itemType, textString)
97+ if (event !== null ) {
98+ consumer. process(event)
99+ }
100+ }
101+ return null
102+ }
103+ }, #[reporterId]);
104+ }
105+
106+ private def RealtimeReporterEvent convert (String itemType , String text ) {
107+ logger. fine(' ' '
108+ ---- «itemType» ----
109+ «text»
110+ ' ' ' )
111+ val docBuilder = DocumentBuilderFactory . newInstance(). newDocumentBuilder()
112+ val doc = docBuilder. parse(new InputSource (new StringReader (text)))
113+ var RealtimeReporterEvent event
114+ if (itemType == " pre-run" ) {
115+ event = doc. convertToPreRunEvent
116+ } else if (itemType == " post-run" ) {
117+ event = doc. convertToPostRunEvent
118+ } else if (itemType == " pre-suite" ) {
119+ event = doc. convertToPreSuiteEvent
120+ } else if (itemType == " post-suite" ) {
121+ event = doc. convertToPostSuiteEvent
122+ } else if (itemType == " pre-test" ) {
123+ event = doc. convertToPreTestEvent
124+ } else if (itemType == " post-test" ) {
125+ event = doc. convertToPostTestEvent
126+ }
127+ return event
128+ }
129+
130+ private def RealtimeReporterEvent convertToPreRunEvent (Document doc ) {
131+ val event = new PreRunEvent
132+ event. totalNumberOfTests = Integer . valueOf(doc. getNode(" /event/totalNumberOfTests" )? . textContent)
133+ val nodeList = doc. getNodeList(" /event/items/*" )
134+ for (i : 0 .. < nodeList. length) {
135+ val node = nodeList. item(i)
136+ if (node. nodeName == " suite" ) {
137+ val suite = new Suite
138+ event. items. add(suite)
139+ suite. populate(node)
140+ } else if (node. nodeName == " test" ) {
141+ val test = new Test
142+ event. items. add(test)
143+ test. populate(node)
144+ }
145+ }
146+ return event
147+ }
148+
149+ private def RealtimeReporterEvent convertToPostRunEvent (Document doc ) {
150+ val event = new PostRunEvent
151+ event. populate(doc. getNode(" /event/run" ))
152+ return event
153+ }
154+
155+ private def RealtimeReporterEvent convertToPreSuiteEvent (Document doc ) {
156+ val event = new PreSuiteEvent
157+ val node = doc. getNode(" /event/suite" )
158+ if (node instanceof Element ) {
159+ event. id = node. attributes? . getNamedItem(" id" )? . nodeValue
160+ }
161+ return event
162+ }
163+
164+ private def RealtimeReporterEvent convertToPostSuiteEvent (Document doc ) {
165+ val event = new PostSuiteEvent
166+ val node = doc. getNode(" /event/suite" )
167+ if (node instanceof Element ) {
168+ event. id = node. attributes? . getNamedItem(" id" )? . nodeValue
169+ event. populate(node)
170+ }
171+ return event
172+ }
173+
174+ private def RealtimeReporterEvent convertToPreTestEvent (Document doc ) {
175+ val event = new PreTestEvent
176+ val node = doc. getNode(" /event/test" )
177+ if (node instanceof Element ) {
178+ event. id = node. attributes? . getNamedItem(" id" )? . nodeValue
179+ event. testNumber = Integer . valueOf(node. getElementsByTagName(" testNumber" )? . item(0 )? . textContent)
180+ event. totalNumberOfTests = Integer . valueOf(node. getElementsByTagName(" totalNumberOfTests" )? . item(0 )? . textContent)
181+ }
182+ return event
183+ }
184+
185+ private def RealtimeReporterEvent convertToPostTestEvent (Document doc ) {
186+ val event = new PostTestEvent
187+ val node = doc. getNode(" /event/test" )
188+ if (node instanceof Element ) {
189+ event. id = node. attributes? . getNamedItem(" id" )? . nodeValue
190+ event. testNumber = Integer . valueOf(node. getElementsByTagName(" testNumber" )? . item(0 )? . textContent)
191+ event. totalNumberOfTests = Integer . valueOf(node. getElementsByTagName(" totalNumberOfTests" )? . item(0 )? . textContent)
192+ event. populate(node)
193+ val failedExpectations = node. getNodeList(" failedExpectations/expectation" )
194+ for (i : 0 .. < failedExpectations. length) {
195+ val expectationNode = failedExpectations. item(i)
196+ val expectation = new Expectation
197+ event. failedExpectations. add(expectation)
198+ expectation. populate(expectationNode)
199+ }
200+ }
201+ return event
202+ }
203+
204+ private def void populate (Suite suite , Node node ) {
205+ if (node instanceof Element ) {
206+ suite. id = node. attributes? . getNamedItem(" id" )? . nodeValue
207+ suite. name = node. getElementsByTagName(" name" )? . item(0 )? . textContent
208+ suite. description = node. getElementsByTagName(" description" )? . item(0 )? . textContent
209+ val nodeList = node. getNodeList(" items/*" )
210+ for (i : 0 .. < nodeList. length) {
211+ val childNode = nodeList. item(i)
212+ if (childNode. nodeName == " suite" ) {
213+ val childSuite = new Suite
214+ suite. items. add(childSuite)
215+ childSuite. populate(childNode)
216+ } else if (childNode. nodeName == " test" ) {
217+ val childTest = new Test
218+ suite. items. add(childTest)
219+ childTest. populate(childNode)
220+ }
221+ }
222+ }
223+ }
224+
225+ private def void populate (Test test , Node node ) {
226+ if (node instanceof Element ) {
227+ test. id = node. attributes? . getNamedItem(" id" )? . nodeValue
228+ test. executionType = node. getElementsByTagName(" executionType" )? . item(0 )? . textContent
229+ test. ownerName = node. getElementsByTagName(" ownerName" )? . item(0 )? . textContent
230+ test. objectName = node. getElementsByTagName(" objectName" )? . item(0 )? . textContent
231+ test. procedureName = node. getElementsByTagName(" procedureName" )? . item(0 )? . textContent
232+ test. disabled = node. getElementsByTagName(" disabled" )? . item(0 )? . textContent == " true"
233+ test. name = node. getElementsByTagName(" name" )? . item(0 )? . textContent
234+ test. description = node. getElementsByTagName(" description" )? . item(0 )? . textContent
235+ test. testNumber = Integer . valueOf(node. getElementsByTagName(" testNumber" )? . item(0 )? . textContent)
236+ }
237+ }
238+
239+ private def void populate (PostEvent event , Node node ) {
240+ if (node instanceof Element ) {
241+ event. startTime = node. getElementsByTagName(" startTime" )? . item(0 )? . textContent
242+ event. endTime = node. getElementsByTagName(" endTime" )? . item(0 )? . textContent
243+ event. executionTime = Double . valueOf(node. getElementsByTagName(" executionTime" )? . item(0 )? . textContent)
244+ event. counter. populate(node)
245+ event. errorStack = node. getElementsByTagName(" errorStack" )? . item(0 )? . textContent
246+ event. serverOutput = node. getElementsByTagName(" serverOutput" )? . item(0 )? . textContent
247+ }
248+ }
249+
250+ private def void populate (Counter counter , Node node ) {
251+ if (node instanceof Element ) {
252+ val counterNode = node. getElementsByTagName(" counter" )? . item(0 )
253+ if (counterNode instanceof Element ) {
254+ counter. disabled = Integer . valueOf(counterNode. getElementsByTagName(" disabled" )? . item(0 )? . textContent)
255+ counter. success = Integer . valueOf(counterNode. getElementsByTagName(" success" )? . item(0 )? . textContent)
256+ counter. failure = Integer . valueOf(counterNode. getElementsByTagName(" failure" )? . item(0 )? . textContent)
257+ counter. error = Integer . valueOf(counterNode. getElementsByTagName(" error" )? . item(0 )? . textContent)
258+ counter. warning = Integer . valueOf(counterNode. getElementsByTagName(" warning" )? . item(0 )? . textContent)
259+ }
260+ }
261+ }
262+
263+ private def void populate (Expectation expectation , Node node ) {
264+ if (node instanceof Element ) {
265+ expectation. description = node. getElementsByTagName(" description" )? . item(0 )? . textContent
266+ expectation. message = node. getElementsByTagName(" message" )? . item(0 )? . textContent
267+ expectation. caller = node. getElementsByTagName(" caller" )? . item(0 )? . textContent
268+ }
269+ }
270+ }
0 commit comments