From 1fd57f925a48a3835618f350fcae190929652f1a Mon Sep 17 00:00:00 2001 From: Javier <10879637+javiertuya@users.noreply.github.com> Date: Sun, 11 Aug 2024 10:53:14 +0200 Subject: [PATCH] Mutation evaluation, java only --- .../Giis.Qacover.Driver/QueryReader.N.cs | 46 ++++++++ .../Giis.Qacover.Driver/StatementAdapter.N.cs | 19 +--- .../Configuration.cs | 14 +++ .../RuleServices.cs | 14 ++- .../Giis.Qacover.Core/CoverageManager.cs | 2 + .../Giis.Qacover.Core/IQueryReader.cs | 24 ++++ .../Giis.Qacover.Core/QueryStatement.cs | 63 +---------- .../Giis.Qacover.Core/RuleDriver.cs | 6 + .../Giis.Qacover.Core/RuleDriverFactory.cs | 11 +- .../Giis.Qacover.Core/RuleDriverFpc.cs | 9 +- .../Giis.Qacover.Core/RuleDriverMutation.cs | 34 ++++++ .../Giis.Qacover.Report/ClassHtmlWriter.cs | 4 +- .../Test4giis.Qacover/TestFaults.cs | 8 +- net/build.xml | 1 + .../qacover-report/all-data-by-run-order.txt | 14 +-- net/resources/qacover-report/index.html | 2 +- .../test4giis.qacoverapp.AppSimpleJdbc.html | 2 +- .../test4giis.qacoverapp.AppSimpleJdbc2.html | 2 +- ...4giis.qacoverapp.AppSimpleJdbc3Errors.html | 8 +- .../giis/qacover/core/CoverageManager.java | 6 +- .../java/giis/qacover/core/IQueryReader.java | 26 +++++ .../giis/qacover/core/QueryStatement.java | 43 +------ .../java/giis/qacover/core/RuleDriver.java | 6 + .../giis/qacover/core/RuleDriverFactory.java | 7 +- .../java/giis/qacover/core/RuleDriverFpc.java | 9 +- .../giis/qacover/core/RuleDriverMutation.java | 32 ++++++ .../qacover/core/services/Configuration.java | 9 ++ .../qacover/core/services/RuleServices.java | 11 +- .../java/giis/qacover/driver/QueryReader.java | 91 +++++++++++++++ .../giis/qacover/driver/StatementAdapter.java | 5 + .../java/test4giis/qacover/TestFaults.java | 12 +- .../qacover/TestMutationEvaluation.java | 105 ++++++++++++++++++ .../java/test4giis/qacoverapp/AppBase.java | 9 ++ .../qacover-report/all-data-by-run-order.txt | 14 +-- ...4giis.qacoverapp.AppSimpleJdbc3Errors.html | 6 +- .../giis/qacover/report/ClassHtmlWriter.java | 6 +- 36 files changed, 517 insertions(+), 163 deletions(-) create mode 100644 net/QACover/Giis.Qacover.Driver/QueryReader.N.cs create mode 100644 net/QACover/Translated/Giis.Qacover.Core/IQueryReader.cs create mode 100644 net/QACover/Translated/Giis.Qacover.Core/RuleDriverMutation.cs create mode 100644 qacover-core/src/main/java/giis/qacover/core/IQueryReader.java create mode 100644 qacover-core/src/main/java/giis/qacover/core/RuleDriverMutation.java create mode 100644 qacover-core/src/main/java/giis/qacover/driver/QueryReader.java create mode 100644 qacover-core/src/test/java/test4giis/qacover/TestMutationEvaluation.java diff --git a/net/QACover/Giis.Qacover.Driver/QueryReader.N.cs b/net/QACover/Giis.Qacover.Driver/QueryReader.N.cs new file mode 100644 index 0000000..a85ce0e --- /dev/null +++ b/net/QACover/Giis.Qacover.Driver/QueryReader.N.cs @@ -0,0 +1,46 @@ +using Giis.Qacover.Core; +using Giis.Qacover.Portable; +using System; +using System.Collections.Generic; +using System.Data.Common; + +namespace Giis.Qacover.Driver +{ + public class QueryReader : IQueryReader + { + DbCommand cmd; + + // This implementation does not takes a connection and sql, + // included both and was created by the StatementAdapter + // because the way in which parameters were replaced + public QueryReader(DbCommand cmd) + { + this.cmd = cmd; + } + public bool HasRows() + { + try + { + DbDataReader reader = cmd.ExecuteReader(); + bool hasNext = reader.Read(); + reader.Close(); + return hasNext; + } + catch (Exception e) // just to produce same exception than java version + { + throw new QaCoverException("QueryReader.hasRows", e); + } + } + + public IList GetRows() + { + throw new System.NotImplementedException(); + } + + public bool EqualRows(IList expected) + { + throw new System.NotImplementedException(); + } + + } +} \ No newline at end of file diff --git a/net/QACover/Giis.Qacover.Driver/StatementAdapter.N.cs b/net/QACover/Giis.Qacover.Driver/StatementAdapter.N.cs index 458c83d..59127e1 100644 --- a/net/QACover/Giis.Qacover.Driver/StatementAdapter.N.cs +++ b/net/QACover/Giis.Qacover.Driver/StatementAdapter.N.cs @@ -64,13 +64,9 @@ public override Connection GetConnection() { return new Connection(nativeConn); } - /** - * Determina si la ejecucion del sql devuelve alguna fila (usada para evaluar reglas fpc) - */ - public override bool HasRows(string sql) + + public override IQueryReader GetQueryReader(String sql) { - try - { DbCommand cmd = this.nativeConn.CreateCommand(); //El remplazo de parametros tiene tres situaciones: //-no hay parametros @@ -86,15 +82,7 @@ public override bool HasRows(string sql) else if (this.parameters != null && this.parameters.Size() > 0) cmd.CommandText = GetSqlWithValues(sql); - DbDataReader reader = cmd.ExecuteReader(); - bool hasNext = reader.Read(); - reader.Close(); - return hasNext; - } - catch (Exception e) - { - throw new QaCoverException("SpyStatementAdapter.hasRows", e); - } + return new QueryReader(cmd); } private void AddParameters(DbCommand cmd, DbParameterCollection parameters) { @@ -114,6 +102,7 @@ private void AddParameters(DbCommand cmd, DbParameterCollection parameters) cmd.Parameters.Add(param); } } + } } diff --git a/net/QACover/Translated/Giis.Qacover.Core.Services/Configuration.cs b/net/QACover/Translated/Giis.Qacover.Core.Services/Configuration.cs index 0f2fcc5..7cd0c90 100644 --- a/net/QACover/Translated/Giis.Qacover.Core.Services/Configuration.cs +++ b/net/QACover/Translated/Giis.Qacover.Core.Services/Configuration.cs @@ -33,6 +33,8 @@ public class Configuration private string storeReportsLocation; + private string ruleServiceType; + private string fpcServiceUrl; private string fpcServiceOptions; @@ -77,6 +79,7 @@ public virtual Giis.Qacover.Core.Services.Configuration Reset() string defaultReportsSubDir = FileUtil.GetPath(Parameters.GetReportSubdir(), QacoverName, "reports"); storeRulesLocation = FileUtil.GetPath(storeRootLocation, GetProperty("qacover.store.rules", defaultRulesSubDir)); storeReportsLocation = FileUtil.GetPath(storeRootLocation, GetProperty("qacover.store.reports", defaultReportsSubDir)); + ruleServiceType = "fpc"; fpcServiceUrl = GetProperty("qacover.fpc.url", "https://in2test.lsi.uniovi.es/tdrules/api/v4"); fpcServiceOptions = GetProperty("qacover.fpc.options", string.Empty); optimizeRuleEvaluation = JavaCs.EqualsIgnoreCase("true", GetProperty("qacover.optimize.rule.evaluation", "false")); @@ -261,6 +264,11 @@ public virtual void SetStoreReportsLocation(string location) storeReportsLocation = location; } + public virtual string GetRuleServiceType() + { + return ruleServiceType; + } + public virtual string GetFpcServiceUrl() { return fpcServiceUrl; @@ -271,6 +279,12 @@ public virtual string GetFpcServiceOptions() return fpcServiceOptions; } + public virtual Giis.Qacover.Core.Services.Configuration SetRuleServiceType(string ruleServiceType) + { + this.ruleServiceType = ruleServiceType; + return this; + } + public virtual Giis.Qacover.Core.Services.Configuration SetFpcServiceOptions(string fpcServiceOptions) { this.fpcServiceOptions = fpcServiceOptions; diff --git a/net/QACover/Translated/Giis.Qacover.Core.Services/RuleServices.cs b/net/QACover/Translated/Giis.Qacover.Core.Services/RuleServices.cs index 208b099..9fa6aae 100644 --- a/net/QACover/Translated/Giis.Qacover.Core.Services/RuleServices.cs +++ b/net/QACover/Translated/Giis.Qacover.Core.Services/RuleServices.cs @@ -54,7 +54,7 @@ private TdRulesApi GetApi() return api; } - public virtual QueryModel GetRulesModel(string sql, SchemaModel schema, string fpcOptions) + public virtual QueryModel GetFpcRulesModel(string sql, SchemaModel schema, string fpcOptions) { this.SetErrorContext("Generate SQLFpc coverage rules"); TdRules model = GetApi().GetRules(schema.GetModel(), sql, fpcOptions); @@ -66,6 +66,18 @@ public virtual QueryModel GetRulesModel(string sql, SchemaModel schema, string f return new QueryModel(model); } + public virtual QueryModel GetMutationRulesModel(string sql, SchemaModel schema, string fpcOptions) + { + this.SetErrorContext("Generate SQLMutation coverage rules"); + TdRules model = GetApi().GetMutants(schema.GetModel(), sql, fpcOptions); + InjectFaultIfNeeded(model); + if (!string.Empty.Equals(model.GetError())) + { + throw new QaCoverException(model.GetError()); + } + return new QueryModel(model); + } + public virtual string GetRulesInput(string sql, SchemaModel schemaModel, string fpcOptions) { TdRulesBody body = new TdRulesBody(); diff --git a/net/QACover/Translated/Giis.Qacover.Core/CoverageManager.cs b/net/QACover/Translated/Giis.Qacover.Core/CoverageManager.cs index 272747f..9ecaca2 100644 --- a/net/QACover/Translated/Giis.Qacover.Core/CoverageManager.cs +++ b/net/QACover/Translated/Giis.Qacover.Core/CoverageManager.cs @@ -126,6 +126,8 @@ public virtual void Run(RuleServices svc, StoreService store, QueryStatement stm model.Reset(); result = new ResultVector(rules.Count); stmt.SetVariant(new Variability(model.GetDbms())); + // Mutation requires a previous step to read query results + ruleDriver.PrepareEvaluation(stmt, stmt.GetSql()); // Executes and annotates the coverage/status of each rule for (int i = 0; i < rules.Count; i++) { diff --git a/net/QACover/Translated/Giis.Qacover.Core/IQueryReader.cs b/net/QACover/Translated/Giis.Qacover.Core/IQueryReader.cs new file mode 100644 index 0000000..754de38 --- /dev/null +++ b/net/QACover/Translated/Giis.Qacover.Core/IQueryReader.cs @@ -0,0 +1,24 @@ +///////////////////////////////////////////////////////////////////////////////////////////// +/////// THIS FILE HAS BEEN AUTOMATICALLY CONVERTED FROM THE JAVA SOURCES. DO NOT EDIT /////// +///////////////////////////////////////////////////////////////////////////////////////////// +using System.Collections.Generic; + + +namespace Giis.Qacover.Core +{ + /// Provides read access to the data stored in the database to evaluate the rules + public interface IQueryReader + { + /// + /// Determines if the execution of a query returns at least one row + /// (to evaluate the fpc coverage) + /// + bool HasRows(); + + /// Gets all rows returned by a sql statement + IList GetRows(); + + /// Determines if the list of rows is the same than the returned by a sql statement + bool EqualRows(IList expected); + } +} diff --git a/net/QACover/Translated/Giis.Qacover.Core/QueryStatement.cs b/net/QACover/Translated/Giis.Qacover.Core/QueryStatement.cs index 58826dd..f8f71f3 100644 --- a/net/QACover/Translated/Giis.Qacover.Core/QueryStatement.cs +++ b/net/QACover/Translated/Giis.Qacover.Core/QueryStatement.cs @@ -6,7 +6,6 @@ using Giis.Portable.Util; using Giis.Qacover.Core.Services; using Giis.Qacover.Model; -using Giis.Qacover.Portable; using Java.Sql; using NLog; @@ -140,36 +139,8 @@ private string ReplaceSingleParameter(string sourceSql, string name, string valu public abstract Connection GetConnection(); - /// - /// Determines if the execution of a query returns at least one row - /// (to evaluate the fpc coverage) - /// - public virtual bool HasRows(string sql) - { - string sqlWithValues = GetSqlWithValues(sql); - // any exception is propagated to detect failures in individual rules - Statement stmt = null; - ResultSet rs = null; - try - { - // do not use try with resources for java 1.6 compatibility - stmt = this.GetConnection().CreateStatement(); - // NOSONAR - stmt.SetMaxRows(1); - rs = stmt.ExecuteQuery(sqlWithValues); - // NOSONAR - return rs.Next(); - } - catch (SQLException e) - { - throw new QaCoverException("SpyStatementAdapter.hasRows", e); - } - finally - { - SafeClose(rs); - SafeClose(stmt); - } - } + /// Returns an object to browse the data accessed from the current connection + public abstract IQueryReader GetQueryReader(string sql); /// /// Determines if the query is a select, needed to filter other statements @@ -187,36 +158,6 @@ public static bool IsSelectQuery(string sql, Logger log) return false; } - private void SafeClose(Statement stmt) - { - try - { - if (stmt != null) - { - stmt.Close(); - } - } - catch (SQLException) - { - } - } - - // no action - private void SafeClose(ResultSet rs) - { - try - { - if (rs != null) - { - rs.Close(); - } - } - catch (SQLException) - { - } - } - - // no action public virtual Exception GetException() { return exception; diff --git a/net/QACover/Translated/Giis.Qacover.Core/RuleDriver.cs b/net/QACover/Translated/Giis.Qacover.Core/RuleDriver.cs index 62b5f87..fa4a915 100644 --- a/net/QACover/Translated/Giis.Qacover.Core/RuleDriver.cs +++ b/net/QACover/Translated/Giis.Qacover.Core/RuleDriver.cs @@ -23,6 +23,12 @@ public abstract class RuleDriver /// Generate the model with the rules public abstract QueryModel GetRules(RuleServices svc, string sql, SchemaModel schema, string fpcOptions); + /// + /// Executes the preliminary actions on the query before evaluating each rule + /// (only needed for mutation) + /// + public abstract void PrepareEvaluation(QueryStatement stmt, string sql); + /// Determines the coverage of a rule, saving the results and returning if it is covered public virtual string EvaluateRule(RuleModel model, QueryStatement stmt) { diff --git a/net/QACover/Translated/Giis.Qacover.Core/RuleDriverFactory.cs b/net/QACover/Translated/Giis.Qacover.Core/RuleDriverFactory.cs index 582910c..7968c8a 100644 --- a/net/QACover/Translated/Giis.Qacover.Core/RuleDriverFactory.cs +++ b/net/QACover/Translated/Giis.Qacover.Core/RuleDriverFactory.cs @@ -1,6 +1,7 @@ ///////////////////////////////////////////////////////////////////////////////////////////// /////// THIS FILE HAS BEEN AUTOMATICALLY CONVERTED FROM THE JAVA SOURCES. DO NOT EDIT /////// ///////////////////////////////////////////////////////////////////////////////////////////// +using Giis.Qacover.Core.Services; namespace Giis.Qacover.Core @@ -17,7 +18,15 @@ public class RuleDriverFactory /// public virtual RuleDriver GetDriver() { - return new RuleDriverFpc(); + if ("mutation".Equals(Configuration.GetInstance().GetRuleServiceType())) + { + return new RuleDriverMutation(); + } + else + { + // fpc + return new RuleDriverFpc(); + } } } } diff --git a/net/QACover/Translated/Giis.Qacover.Core/RuleDriverFpc.cs b/net/QACover/Translated/Giis.Qacover.Core/RuleDriverFpc.cs index f1fde33..897549d 100644 --- a/net/QACover/Translated/Giis.Qacover.Core/RuleDriverFpc.cs +++ b/net/QACover/Translated/Giis.Qacover.Core/RuleDriverFpc.cs @@ -15,12 +15,17 @@ public class RuleDriverFpc : RuleDriver { public override QueryModel GetRules(RuleServices svc, string sql, SchemaModel schema, string fpcOptions) { - return svc.GetRulesModel(sql, schema, fpcOptions); + return svc.GetFpcRulesModel(sql, schema, fpcOptions); } + public override void PrepareEvaluation(QueryStatement stmt, string sql) + { + } + + // no preparation actions, evaluation is made by checking number of rows for each rule protected internal override bool IsCovered(QueryStatement stmt, string sql) { - return stmt.HasRows(sql); + return stmt.GetQueryReader(sql).HasRows(); } } } diff --git a/net/QACover/Translated/Giis.Qacover.Core/RuleDriverMutation.cs b/net/QACover/Translated/Giis.Qacover.Core/RuleDriverMutation.cs new file mode 100644 index 0000000..0457385 --- /dev/null +++ b/net/QACover/Translated/Giis.Qacover.Core/RuleDriverMutation.cs @@ -0,0 +1,34 @@ +///////////////////////////////////////////////////////////////////////////////////////////// +/////// THIS FILE HAS BEEN AUTOMATICALLY CONVERTED FROM THE JAVA SOURCES. DO NOT EDIT /////// +///////////////////////////////////////////////////////////////////////////////////////////// +using System.Collections.Generic; +using Giis.Qacover.Core.Services; +using Giis.Qacover.Model; + + +namespace Giis.Qacover.Core +{ + /// + /// Delegate that performs the required actions + /// on the SQLMutation rules (get and evaluate the rules) + /// + public class RuleDriverMutation : RuleDriver + { + private IList rows; + + public override QueryModel GetRules(RuleServices svc, string sql, SchemaModel schema, string fpcOptions) + { + return svc.GetMutationRulesModel(sql, schema, fpcOptions); + } + + public override void PrepareEvaluation(QueryStatement stmt, string sql) + { + this.rows = stmt.GetQueryReader(sql).GetRows(); + } + + protected internal override bool IsCovered(QueryStatement stmt, string sql) + { + return stmt.GetQueryReader(sql).EqualRows(this.rows); + } + } +} diff --git a/net/QACover/Translated/Giis.Qacover.Report/ClassHtmlWriter.cs b/net/QACover/Translated/Giis.Qacover.Report/ClassHtmlWriter.cs index 3f2c9e0..27d987c 100644 --- a/net/QACover/Translated/Giis.Qacover.Report/ClassHtmlWriter.cs +++ b/net/QACover/Translated/Giis.Qacover.Report/ClassHtmlWriter.cs @@ -94,8 +94,8 @@ public virtual string GetRulesContent(string rulesContent) public virtual string GetRuleContent(RuleModel rule) { string template = " \n" + " \n" + " $ruleId$ - dead: $ruleDead$ count: $ruleCount$\n" + " \n" + " category: $ruleCategory$ type: $ruleType$ subtype: $ruleSubtype$ location: $ruleLocation$\n" - + "
(run params not available)
\n" + " \n" + " \n" + " \n" + " \n" + " $ruleDescription$\n" + " $ruleSql$\n" - + " \n" + " $ruleErrors$"; + + "
(run params not available)
\n" + " \n" + " \n" + " \n" + " \n" + (string.Empty.Equals(rule.GetDescription()) ? " $ruleSql$\n" : + " $ruleDescription$\n" + " $ruleSql$\n") + " \n" + " $ruleErrors$"; // needs a complete row to allow error message span across description and sql columns return template.Replace("$ruleId$", rule.GetId()).Replace("$ruleDead$", rule.GetDead().ToString()).Replace("$ruleCount$", rule.GetCount().ToString()).Replace("$ruleCategory$", rule.GetCategory()).Replace("$ruleType$", rule.GetMainType()).Replace("$ruleSubtype$", rule.GetSubtype()) .Replace("$ruleLocation$", Encode(rule.GetLocation())).Replace("$ruleStatus$", rule.GetDead() > 0 ? "covered" : "uncovered").Replace("$ruleDescription$", GetDescriptionHtml(Encode(rule.GetDescription()))).Replace("$ruleSql$", GetSqlHtml(Encode(rule.GetSql()))).Replace("$ruleErrors$" diff --git a/net/QACoverTest/Translated/Test4giis.Qacover/TestFaults.cs b/net/QACoverTest/Translated/Test4giis.Qacover/TestFaults.cs index 8e51407..3855a21 100644 --- a/net/QACoverTest/Translated/Test4giis.Qacover/TestFaults.cs +++ b/net/QACoverTest/Translated/Test4giis.Qacover/TestFaults.cs @@ -191,8 +191,8 @@ public virtual void TestFaultServiceRunRules() IList rules = model.GetRules(); //NOSONAR no using typed names for compatibility with downgrade to jdk 1.4 string errorString = rules[0].GetErrorString(); - AssertExceptionMessage(variant.IsJava() ? (variant.IsJava8() ? "giis.qacover.portable.QaCoverException: SpyStatementAdapter.hasRows. Caused by: org.sqlite.SQLiteException: [SQLITE_ERROR] SQL error or missing database (near \"selectar\": syntax error)" : "giis.qacover.portable.QaCoverException: SpyStatementAdapter.hasRows. Caused by: org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement" - ) : "Giis.Qacover.Portable.QaCoverException: SpyStatementAdapter.hasRows. Caused by: System.Data.SQLite.SQLiteException: SQL logic error\nnear \"selectar\": syntax error", errorString); + AssertExceptionMessage(variant.IsJava() ? (variant.IsJava8() ? "giis.qacover.portable.QaCoverException: QueryReader.hasRows. Caused by: org.sqlite.SQLiteException: [SQLITE_ERROR] SQL error or missing database (near \"selectar\": syntax error)" : "giis.qacover.portable.QaCoverException: QueryReader.hasRows. Caused by: org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement" + ) : "Giis.Qacover.Portable.QaCoverException: QueryReader.hasRows. Caused by: System.Data.SQLite.SQLiteException: SQL logic error\nnear \"selectar\": syntax error", errorString); } //: "giis.Qacover.Portable.QaCoverException: SpyStatementAdapter.hasRows. Caused by: Microsoft.Data.Sqlite.SqliteException: SQLite Error 1: 'near \"selectar\": syntax error'." @@ -220,8 +220,8 @@ public virtual void TestMultipleFaultsQuery() AssertMultipleFaults(true, "qerror=1,count=0,dead=0", "Error at : giis.qacover.portable.QaCoverException: Injected unexpected exception"); } - internal string exceptionInvalidSql = new Variability().IsJava() ? (new Variability().IsJava8() ? "giis.qacover.portable.QaCoverException: SpyStatementAdapter.hasRows. Caused by: org.sqlite.SQLiteException: [SQLITE_ERROR] SQL error or missing database (near \"selectar\": syntax error)" - : "giis.qacover.portable.QaCoverException: SpyStatementAdapter.hasRows. Caused by: org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement") : "Giis.Qacover.Portable.QaCoverException: SpyStatementAdapter.hasRows. Caused by: System.Data.SQLite.SQLiteException: SQL logic error\nnear \"selectar\": syntax error"; + internal string exceptionInvalidSql = new Variability().IsJava() ? (new Variability().IsJava8() ? "giis.qacover.portable.QaCoverException: QueryReader.hasRows. Caused by: org.sqlite.SQLiteException: [SQLITE_ERROR] SQL error or missing database (near \"selectar\": syntax error)" : + "giis.qacover.portable.QaCoverException: QueryReader.hasRows. Caused by: org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement") : "Giis.Qacover.Portable.QaCoverException: QueryReader.hasRows. Caused by: System.Data.SQLite.SQLiteException: SQL logic error\nnear \"selectar\": syntax error"; //: "giis.Qacover.Portable.QaCoverException: SpyStatementAdapter.hasRows. Caused by: Microsoft.Data.Sqlite.SqliteException: SQLite Error 1: 'near \"selectar\": syntax error'." /// diff --git a/net/build.xml b/net/build.xml index 5a121b9..b99fbea 100644 --- a/net/build.xml +++ b/net/build.xml @@ -41,6 +41,7 @@ +