[System.Data] Integration tests (#4538)
[mono.git] / mcs / class / System.Data / Test / ProviderTests / Common / ConnectionManager.cs
1 // ConnectionManager.cs - Singleton ConnectionManager class to manage
2 // database connections for test cases.
3 //
4 // Authors:
5 //      Sureshkumar T (tsureshkumar@novell.com)
6 // 
7 // Copyright Novell Inc., and the individuals listed on the
8 // ChangeLog entries.
9 //
10 //
11 // Permission is hereby granted, free of charge, to any person
12 // obtaining a copy of this software and associated documentation
13 // files (the "Software"), to deal in the Software without
14 // restriction, including without limitation the rights to use, copy,
15 // modify, merge, publish, distribute, sublicense, and/or sell copies
16 // of the Software, and to permit persons to whom the Software is
17 // furnished to do so, subject to the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
26 // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
27 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
28 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 // SOFTWARE.
30
31 using System;
32 using System.Collections.Generic;
33 using System.Configuration;
34 using System.Data;
35 using System.Data.Common;
36 using System.Data.Odbc;
37 using System.Data.SqlClient;
38 using System.IO;
39 using System.Linq;
40 using System.Text.RegularExpressions;
41 using NUnit.Framework;
42
43 namespace MonoTests.System.Data.Connected
44 {
45         public class ConnectionManager
46         {
47                 private static ConnectionManager instance;
48                 private ConnectionHolder<OdbcConnection> odbc;
49                 private ConnectionHolder<SqlConnection> sql;
50
51                 private const string OdbcEnvVar = "SYSTEM_DATA_ODBC";
52                 private const string SqlEnvVar = "SYSTEM_DATA_MSSQL";
53
54                 private ConnectionManager ()
55                 {
56                         //Environment.SetEnvironmentVariable(OdbcEnvVar, @"mysql-odbc|Driver={MySQL ODBC 5.3 Unicode Driver};server=127.0.0.1;uid=sa;pwd=qwerty123;");
57                         //Environment.SetEnvironmentVariable(SqlEnvVar, @"sqlserver-tds|server=127.0.0.1;database=master;user id=sa;password=qwerty123");
58
59                         // Generate a random db name
60                         DatabaseName = "monotest" + Guid.NewGuid().ToString().Substring(0, 7);
61
62                         sql = ConnectionHolder<SqlConnection>.FromEnvVar(SqlEnvVar);
63                         if (sql != null)
64                                 CreateMssqlDatabase();
65                         
66                         odbc = ConnectionHolder<OdbcConnection>.FromEnvVar(OdbcEnvVar);
67                         if (odbc != null)
68                                 CreateMysqlDatabase();
69                 }
70
71                 private void CreateMssqlDatabase()
72                 {
73                         DBHelper.ExecuteNonQuery(sql.Connection, $"CREATE DATABASE [{DatabaseName}]");
74                         sql.Connection.ChangeDatabase(DatabaseName);
75
76                         string query = File.ReadAllText(@"Test/ProviderTests/sql/sqlserver.sql");
77
78                         var queries = SplitSqlStatements(query);
79                         foreach (var subQuery in queries)
80                         {
81                                 DBHelper.ExecuteNonQuery(sql.Connection, subQuery);
82                         }
83                 }
84
85                 private void CreateMysqlDatabase()
86                 {
87                         DBHelper.ExecuteNonQuery(odbc.Connection, $"CREATE DATABASE {DatabaseName}");
88                         odbc.Connection.ChangeDatabase(DatabaseName);
89                         odbc.ConnectionString += $"database={DatabaseName}";
90
91                         string query = File.ReadAllText("Test/ProviderTests/sql/MySQL_5.sql");
92
93                         var groups = query.Replace("delimiter ", "")
94                                 .Split(new[] { "//\n" }, StringSplitOptions.RemoveEmptyEntries);
95
96                         foreach (var subQuery in groups[0].Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries).Concat(groups.Skip(1)))
97                         {
98                                 DBHelper.ExecuteNonQuery(odbc.Connection, subQuery);
99                         }
100                 }
101
102                 private void DropMssqlDatabase()
103                 {
104                         sql.Connection.ChangeDatabase("master");
105                         string query = $"ALTER DATABASE [{DatabaseName}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;\nDROP DATABASE [{DatabaseName}]";
106                         DBHelper.ExecuteNonQuery(sql.Connection, query);
107                 }
108
109                 private void DropMysqlDatabase()
110                 {
111                         string query = $"DROP DATABASE [{DatabaseName}]";
112                         DBHelper.ExecuteNonQuery(odbc.Connection, query);
113                 }
114
115                 // Split SQL script by "GO" statements
116                 private static IEnumerable<string> SplitSqlStatements(string sqlScript)
117                 {
118                         var statements = Regex.Split(sqlScript,
119                                         $@"^[\t ]*GO[\t ]*\d*[\t ]*(?:--.*)?$",
120                                         RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase);
121                         return statements.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim(' ', '\r', '\n'));
122                 }
123
124                 public static ConnectionManager Instance => instance ?? (instance = new ConnectionManager());
125
126                 public string DatabaseName { get; }
127
128                 public ConnectionHolder<OdbcConnection> Odbc
129                 {
130                         get
131                         {
132                                 if (odbc == null)
133                                         Assert.Ignore($"{OdbcEnvVar} environment variable is not set");
134                                 return odbc;
135                         }
136                 }
137
138                 public ConnectionHolder<SqlConnection> Sql
139                 {
140                         get
141                         {
142                                 if (sql == null)
143                                         Assert.Ignore($"{SqlEnvVar} environment variable is not set");
144                                 return sql;
145                         }
146                 }
147
148                 public void Close()
149                 {
150                         sql?.CloseConnection();
151                         odbc?.CloseConnection();
152                 }
153         }
154
155         public class ConnectionHolder<TConnection> where TConnection : DbConnection
156         {
157                 private TConnection connection;
158
159                 public EngineConfig EngineConfig { get; }
160
161                 public TConnection Connection
162                 {
163                         get
164                         {
165                                 if (!(connection.State == ConnectionState.Closed || 
166                                         connection.State == ConnectionState.Broken))
167                                         connection.Close();
168                                 connection.ConnectionString = ConnectionString;
169                                 connection.Open();
170                                 return connection;
171                         }
172                 }
173
174                 public void CloseConnection()
175                 {
176                         if (connection != null && connection.State != ConnectionState.Closed)
177                                 connection.Close();
178                 }
179
180                 public string ConnectionString { get; set; }
181
182                 public ConnectionHolder(EngineConfig engineConfig, DbProviderFactory dbProviderFactory, string connectionString)
183                 {
184                         EngineConfig = engineConfig;
185                         connection = (TConnection)dbProviderFactory.CreateConnection();
186                         ConnectionString = connectionString;
187                 }
188
189                 public static ConnectionHolder<TConnection> FromEnvVar(string envVarName)
190                 {
191                         string variable = Environment.GetEnvironmentVariable(envVarName) ?? string.Empty;
192                         var envParts = variable.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
193                         if (envParts.Length == 0 || string.IsNullOrEmpty(envParts[0]))
194                                 return null;
195
196                         string connectionName = envParts[0];
197                         string connectionString = variable.Remove(0, envParts[0].Length + 1);
198
199                         ConnectionConfig[] connections = null;
200                         try
201                         {
202                                 connections = (ConnectionConfig[]) ConfigurationManager.GetSection("providerTests");
203                         }
204                         catch
205                         {
206                                 return null;
207                         }
208
209                         foreach (ConnectionConfig connConfig in connections)
210                         {
211                                 if (connConfig.Name != connectionName)
212                                         continue;
213
214                                 DbProviderFactory factory = DbProviderFactories.GetFactory(connConfig.Factory);
215                                 return new ConnectionHolder<TConnection>(connConfig.Engine, factory, connectionString);
216                         }
217                         throw new InvalidOperationException($"Connection {connectionName} not found");
218                 }
219         }
220 }