1+ // Copyright (C) 2011-2021 Xtensive LLC.
2+ // This code is distributed under MIT license terms.
3+ // See the License.txt file in the project root for more information.
4+ // Created by: Malisa Ncube
5+ // Created: 2011.03.17
6+
7+ using System ;
8+ using System . Data ;
9+ using System . Diagnostics ;
10+ using System . Linq ;
11+ using NUnit . Framework ;
12+ using Xtensive . Core ;
13+ using Xtensive . Sql ;
14+ using Xtensive . Sql . Compiler ;
15+ using Xtensive . Sql . Model ;
16+
17+ namespace Xtensive . Orm . Tests . Sql
18+ {
19+ public abstract class ChinookTestBase
20+ {
21+ protected struct DbCommandExecutionResult
22+ {
23+ public int FieldCount ;
24+ public string [ ] FieldNames ;
25+ public int RowCount ;
26+
27+ public override string ToString ( )
28+ {
29+ if ( FieldNames == null )
30+ FieldNames = new string [ 0 ] ;
31+ return string . Format ( "Fields: '{0}'; Rows: {1}" , string . Join ( "', '" , FieldNames ) , RowCount ) ;
32+ }
33+ }
34+
35+ protected SqlDriver sqlDriver ;
36+ protected SqlConnection sqlConnection ;
37+
38+ protected virtual bool InMemory => false ;
39+
40+ protected virtual bool PerformanceCheck => false ;
41+
42+ protected int RunsPerGroup = 50 ;
43+
44+ protected string Url => TestConnectionInfoProvider . GetConnectionUrl ( ) ;
45+
46+ protected Catalog Catalog { get ; private set ; }
47+
48+ [ OneTimeSetUp ]
49+ public virtual void SetUp ( )
50+ {
51+ CheckRequirements ( ) ;
52+ sqlDriver = TestSqlDriver . Create ( Url ) ;
53+ sqlConnection = sqlDriver . CreateConnection ( ) ;
54+ if ( InMemory ) {
55+ var catalog = new Catalog ( "Dummy" ) ;
56+ var schema = catalog . CreateSchema ( "default" ) ;
57+
58+ var creator = new ChinookSchemaCreator ( sqlDriver ) ;
59+ creator . CreateSchemaContent ( schema ) ;
60+ Catalog = catalog ;
61+ return ;
62+ }
63+
64+ try {
65+ sqlConnection . Open ( ) ;
66+ }
67+ catch ( Exception exception ) {
68+ Console . WriteLine ( exception ) ;
69+ throw ;
70+ }
71+ try {
72+ sqlConnection . BeginTransaction ( ) ;
73+ Catalog = sqlDriver . ExtractCatalog ( sqlConnection ) ;
74+ var schema = Catalog . DefaultSchema ;
75+
76+ var creator = new ChinookSchemaCreator ( sqlDriver ) ;
77+ creator . DropSchemaContent ( sqlConnection , schema ) ;
78+ creator . CreateSchemaContent ( sqlConnection , schema ) ;
79+
80+ sqlConnection . Commit ( ) ;
81+ }
82+ catch {
83+ sqlConnection . Rollback ( ) ;
84+ throw ;
85+ }
86+ }
87+
88+ [ OneTimeTearDown ]
89+ public void TearDown ( )
90+ {
91+ try {
92+ if ( sqlConnection != null && sqlConnection . State != ConnectionState . Closed )
93+ sqlConnection . Close ( ) ;
94+ }
95+ catch ( Exception ex ) {
96+ Console . WriteLine ( ex . Message ) ;
97+ }
98+ }
99+
100+ protected virtual void CheckRequirements ( )
101+ {
102+ }
103+
104+ protected static DbCommandExecutionResult GetExecuteDataReaderResult ( IDbCommand cmd )
105+ {
106+ var result = new DbCommandExecutionResult ( ) ;
107+ try {
108+ cmd . Transaction = cmd . Connection . BeginTransaction ( ) ;
109+ var rowCount = 0 ;
110+ var fieldCount = 0 ;
111+ var fieldNames = new string [ 0 ] ;
112+ using ( var reader = cmd . ExecuteReader ( ) ) {
113+ while ( reader . Read ( ) ) {
114+ if ( rowCount == 0 ) {
115+ fieldCount = reader . FieldCount ;
116+ fieldNames = new string [ fieldCount ] ;
117+ for ( var i = 0 ; i < fieldCount ; i ++ )
118+ fieldNames [ i ] = reader . GetName ( i ) ;
119+ }
120+ rowCount ++ ;
121+ }
122+ }
123+
124+ result . RowCount = rowCount ;
125+ result . FieldCount = fieldCount ;
126+ result . FieldNames = fieldNames ;
127+ }
128+ finally {
129+ cmd . Transaction . Rollback ( ) ;
130+ }
131+ return result ;
132+ }
133+
134+ protected static DbCommandExecutionResult GetExecuteNonQueryResult ( IDbCommand cmd )
135+ {
136+ var result = new DbCommandExecutionResult ( ) ;
137+ try {
138+ cmd . Transaction = cmd . Connection . BeginTransaction ( ) ;
139+ result . RowCount = cmd . ExecuteNonQuery ( ) ;
140+ }
141+ finally {
142+ cmd . Transaction . Rollback ( ) ;
143+ }
144+ return result ;
145+ }
146+
147+ protected string Compile ( ISqlCompileUnit statement )
148+ {
149+ return PerformanceCheck
150+ ? CompileWithPerformanceCheck ( statement )
151+ : CompileRegular ( statement ) ;
152+ }
153+
154+ protected string CompileWithPerformanceCheck ( ISqlCompileUnit statement )
155+ {
156+ var runs = new TimeSpan [ RunsPerGroup * 5 ] ;
157+ var stopwatch = new Stopwatch ( ) ;
158+ var compiledCommandText = sqlDriver . Compile ( statement ) . GetCommandText ( ) ;
159+ for ( var i = 0 ; i < RunsPerGroup * 5 ; i ++ ) {
160+ stopwatch . Start ( ) ;
161+ _ = sqlDriver . Compile ( statement ) . GetCommandText ( ) ;
162+ stopwatch . Stop ( ) ;
163+
164+ runs [ i ] = stopwatch . Elapsed ;
165+ stopwatch . Reset ( ) ;
166+ }
167+
168+ WriteDetailedStats ( runs ) ;
169+ return compiledCommandText ;
170+ }
171+
172+ protected string CompileRegular ( ISqlCompileUnit statement )
173+ {
174+ var stopwatch = new Stopwatch ( ) ;
175+ stopwatch . Start ( ) ;
176+ var compiledStatement = sqlDriver . Compile ( statement ) ;
177+ stopwatch . Stop ( ) ;
178+ WriteDuration ( stopwatch ) ;
179+ return compiledStatement . GetCommandText ( ) ;
180+ }
181+
182+ private void WriteDuration ( Stopwatch stopwatch ) =>
183+ Console . WriteLine ( $ "Compilation elapsed ticks: { stopwatch . Elapsed } ({ stopwatch . ElapsedTicks } ticks)") ;
184+
185+ private void WriteDetailedStats ( TimeSpan [ ] values )
186+ {
187+ var runs = RunsPerGroup ;
188+ var part1 = new ArraySegment < TimeSpan > ( values , runs * 0 , runs ) ;
189+ var part2 = new ArraySegment < TimeSpan > ( values , runs * 1 , runs ) ;
190+ var part3 = new ArraySegment < TimeSpan > ( values , runs * 2 , runs ) ;
191+ var part4 = new ArraySegment < TimeSpan > ( values , runs * 3 , runs ) ;
192+ var part5 = new ArraySegment < TimeSpan > ( values , runs * 4 , runs ) ;
193+
194+ var orderedTicks = values . Select ( ts => ts . Ticks ) . OrderBy ( t => t ) . ToArray ( values . Length ) ;
195+ var min = orderedTicks [ 0 ] ;
196+ var max = orderedTicks [ ^ 1 ] ;
197+ var avg = orderedTicks . Average ( ) ;
198+
199+ var p95barier = ( int ) ( 0.95 * values . Length ) ;
200+ var p95Values = new ArraySegment < long > ( orderedTicks , 0 , p95barier ) ;
201+ var p95Max = p95Values [ ^ 1 ] ;
202+ var p95Avg = p95Values . Average ( ) ;
203+ var deltas = values . Select ( d => Math . Abs ( avg - d . Ticks ) ) ;
204+
205+ Console . WriteLine ( "-------------------------------------" ) ;
206+ Console . WriteLine ( $ "Min: { min } ticks({ new TimeSpan ( min ) } )") ;
207+ Console . WriteLine ( $ "Max: { max } ticks({ new TimeSpan ( max ) } )") ;
208+ Console . WriteLine ( $ "P95Max { p95Max } ticks({ new TimeSpan ( p95Max ) } )") ;
209+ Console . WriteLine ( $ "Avg: { avg } ticks") ;
210+ Console . WriteLine ( $ "P95Avg: { p95Avg } ticks") ;
211+
212+ Console . WriteLine ( "-------------------------------------" ) ;
213+
214+ Console . WriteLine ( "--------------Raw values-------------" ) ;
215+ for ( var i = 0 ; i < part1 . Count ; i ++ ) {
216+ Console . WriteLine ( $ "{ part1 [ i ] . Ticks } { part2 [ i ] . Ticks } { part3 [ i ] . Ticks } { part4 [ i ] . Ticks } { part5 [ i ] . Ticks } ") ;
217+ }
218+ Console . WriteLine ( "-------------------------------------" ) ;
219+ }
220+ }
221+ }
0 commit comments