From 36fc928f963f75ca6a465f4d05480872e319d6e4 Mon Sep 17 00:00:00 2001 From: Pranav Gaikwad Date: Thu, 14 Mar 2024 10:48:21 -0400 Subject: [PATCH] :sparkles: convert windup tests to new format Signed-off-by: Pranav Gaikwad --- Dockerfile | 4 +- go.mod | 24 ++- go.sum | 51 +++-- main.go | 6 +- pkg/conversion/convert.go | 401 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 454 insertions(+), 32 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6e055004..60e1cead 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,8 +3,8 @@ WORKDIR /windup-shim COPY go.mod /windup-shim COPY go.sum /windup-shim -COPY pkg /windup-shim/pkg -COPY main.go /windup-shim +COPY pkg /windup-shim/pkg +COPY main.go /windup-shim RUN go build -o windup-shim main.go diff --git a/go.mod b/go.mod index 34a61fa9..4bb86185 100644 --- a/go.mod +++ b/go.mod @@ -3,35 +3,39 @@ module github.com/fabianvf/windup-rulesets-yaml go 1.18 require ( - github.com/konveyor/analyzer-lsp v0.3.0-beta.1 - go.lsp.dev/uri v0.3.0 + github.com/konveyor-ecosystem/kantra v0.0.0-20240402144049-893ca13c2cd1 + github.com/konveyor/analyzer-lsp v0.3.0 gopkg.in/yaml.v2 v2.4.0 ) require ( github.com/PaesslerAG/gval v1.2.2 // indirect + github.com/bombsimon/logrusr/v3 v3.1.0 // indirect github.com/cbroglie/mustache v1.4.0 // indirect github.com/getkin/kin-openapi v0.108.0 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/swag v0.19.5 // indirect + github.com/go-openapi/jsonpointer v0.20.2 // indirect + github.com/go-openapi/swag v0.22.8 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/hashicorp/go-version v1.6.0 // indirect - github.com/invopop/yaml v0.1.0 // indirect - github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e // indirect + github.com/invopop/yaml v0.2.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + go.lsp.dev/uri v0.3.0 // indirect go.opentelemetry.io/otel v1.11.2 // indirect go.opentelemetry.io/otel/exporters/jaeger v1.11.2 // indirect go.opentelemetry.io/otel/sdk v1.11.2 // indirect go.opentelemetry.io/otel/trace v1.11.2 // indirect - golang.org/x/net v0.8.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/text v0.8.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect google.golang.org/grpc v1.54.0 // indirect google.golang.org/protobuf v1.30.0 // indirect - gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 65fe85eb..1d90ae74 100644 --- a/go.sum +++ b/go.sum @@ -2,7 +2,8 @@ github.com/PaesslerAG/gval v1.2.2 h1:Y7iBzhgE09IGTt5QgGQ2IdaYYYOU134YGHBThD+wm9E github.com/PaesslerAG/gval v1.2.2/go.mod h1:XRFLwvmkTEdYziLdaCeCa5ImcGVrfQbeNUbVR+C6xac= github.com/PaesslerAG/jsonpath v0.1.0 h1:gADYeifvlqK3R3i2cR5B4DGgxLXIPb3TRTH1mGi0jPI= github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8= -github.com/bombsimon/logrusr/v3 v3.0.0 h1:tcAoLfuAhKP9npBxWzSdpsvKPQt1XV02nSf2lZA82TQ= +github.com/bombsimon/logrusr/v3 v3.1.0 h1:zORbLM943D+hDMGgyjMhSAz/iDz86ZV72qaak/CA0zQ= +github.com/bombsimon/logrusr/v3 v3.1.0/go.mod h1:PksPPgSFEL2I52pla2glgCyyd2OqOHAnFF5E+g8Ixco= github.com/cbroglie/mustache v1.4.0 h1:Azg0dVhxTml5me+7PsZ7WPrQq1Gkf3WApcHMjMprYoU= github.com/cbroglie/mustache v1.4.0/go.mod h1:SS1FTIghy0sjse4DUVGV1k/40B1qE1XkD9DtDsHo9iM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -15,10 +16,12 @@ github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= +github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= +github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw= +github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= @@ -28,34 +31,44 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= -github.com/konveyor/analyzer-lsp v0.3.0-beta.1 h1:tAQ7e/2z+eoLERhawXOont9XA2kqYa3BaqVreaFYBtE= -github.com/konveyor/analyzer-lsp v0.3.0-beta.1/go.mod h1:dhFFu6r2W7xIm90T2x0CaUqDh/BkFMeDmYTgVxKJn18= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= +github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/konveyor-ecosystem/kantra v0.0.0-20240402144049-893ca13c2cd1 h1:MKalDQwwryOAUOYvcLHdLUHZ/K8C25MV4918pXpkWRM= +github.com/konveyor-ecosystem/kantra v0.0.0-20240402144049-893ca13c2cd1/go.mod h1:QcfSG8yhfkfRQvbiX1ai0jwzX+fMQ5oHBlNpbjI9RpY= +github.com/konveyor/analyzer-lsp v0.3.0 h1:6B2yecVQ545hL0rQ6l/gyn7ae+IRhUG9g0j/u0fqodI= +github.com/konveyor/analyzer-lsp v0.3.0/go.mod h1:zJCmIq08X0kPvtU8ZSmz+mZmQfBt4hdy9enoEy1AQw4= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= go.lsp.dev/uri v0.3.0 h1:KcZJmh6nFIBeJzTugn5JTU6OOyG0lDOo3R9KwTxTYbo= go.lsp.dev/uri v0.3.0/go.mod h1:P5sbO1IQR+qySTWOCnhnK7phBx+W3zbLqSMDJNTw88I= go.opentelemetry.io/otel v1.11.2 h1:YBZcQlsVekzFsFbjygXMOXSs6pialIZxcjfO/mBDmR0= @@ -66,12 +79,13 @@ go.opentelemetry.io/otel/sdk v1.11.2 h1:GF4JoaEx7iihdMFu30sOyRx52HDHOkl9xQ8SMqNX go.opentelemetry.io/otel/sdk v1.11.2/go.mod h1:wZ1WxImwpq+lVRo4vsmSOxdd+xwoUJ6rqyLc3SyX9aU= go.opentelemetry.io/otel/trace v1.11.2 h1:Xf7hWSF2Glv0DE3MH7fBHvtpSBsjcBUe5MYAmZM/+y0= go.opentelemetry.io/otel/trace v1.11.2/go.mod h1:4N+yC7QEz7TTsG9BSRLNAa63eg5E06ObSbKPmxQ/pKA= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= @@ -83,8 +97,7 @@ google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cn google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/main.go b/main.go index 10368034..2351e235 100644 --- a/main.go +++ b/main.go @@ -47,7 +47,11 @@ func main() { if err != nil { fmt.Println(err) } - _, err = conversion.ConvertWindupRulesetsToAnalyzer(rulesets, location, outputDir, flattenRulesets, false) + analyzerRulesets, err := conversion.ConvertWindupRulesetsToAnalyzer(rulesets, location, outputDir, flattenRulesets, false) + if err != nil { + log.Fatal(err) + } + err = conversion.ConvertWindupRuletestsToAnalyzer(ruletests, rulesets, analyzerRulesets) if err != nil { log.Fatal(err) } diff --git a/pkg/conversion/convert.go b/pkg/conversion/convert.go index 11d357f6..cec93f62 100644 --- a/pkg/conversion/convert.go +++ b/pkg/conversion/convert.go @@ -2,7 +2,9 @@ package conversion import ( "fmt" + "math" "os" + "os/exec" "path/filepath" "reflect" "regexp" @@ -11,6 +13,7 @@ import ( "unicode" "github.com/fabianvf/windup-rulesets-yaml/pkg/windup" + "github.com/konveyor-ecosystem/kantra/pkg/testing" "github.com/konveyor/analyzer-lsp/engine" engineLabels "github.com/konveyor/analyzer-lsp/engine/labels" "github.com/konveyor/analyzer-lsp/output/v1/konveyor" @@ -21,6 +24,7 @@ type analyzerRules struct { rules []map[string]interface{} metadata windup.Metadata relativePath string + sourcePath string } func ConvertWindupRulesetsToAnalyzer(windups []windup.Ruleset, baseLocation, outputDir string, flattenRulesets bool, writeDisoveryRule bool) (map[string]*analyzerRules, error) { @@ -56,6 +60,7 @@ func ConvertWindupRulesetsToAnalyzer(windups []windup.Ruleset, baseLocation, out rules: []map[string]interface{}{}, metadata: windupRuleset.Metadata, relativePath: rulesetRelativePath, + sourcePath: windupRuleset.SourceFile, } } outputRulesets[yamlPath].rules = append(outputRulesets[yamlPath].rules, ruleset...) @@ -83,6 +88,402 @@ func ConvertWindupRulesetsToAnalyzer(windups []windup.Ruleset, baseLocation, out return outputRulesets, nil } +// ConvertWindupRuletestsToAnalyzer converts windup XML tests into the new YAML format +func ConvertWindupRuletestsToAnalyzer(windupTests []windup.Ruletest, windupRulesets []windup.Ruleset, analyzerRulesets map[string]*analyzerRules) error { + analyzerTests := []testing.TestsFile{} + totalTestsFound := 0 + validTests := 0 + // findConvertedPath given path of a XML rules file, return path of the YAML rules file we converted + findConvertedPath := func(path string, analyzerRulesets map[string]*analyzerRules) string { + for yamlPath, ruleset := range analyzerRulesets { + if strings.Contains(ruleset.sourcePath, path) { + return yamlPath + } + } + return "" + } + // one windup tests file can have tests for multiple rules files + // we want to group tests by distinct rules files. moreover, there + // could be multiple windup tests testing the same rule, we group + // tests by rules and convert them into test cases of the same rule instead + analyzerTestsFile := map[string]*testing.TestsFile{} + for _, testsFile := range windupTests { + if testsFile.RulePath == nil || len(testsFile.RulePath) == 0 { + testsFile.RulePath = []string{ + strings.Replace( + strings.Replace(testsFile.SourceFile, "test.xml", "xml", 1), + "tests/", "", 1), + } + } + for _, windupTests := range testsFile.Ruleset { + for _, windupTestCase := range windupTests.Rules.Rule { + totalTestsFound += 1 + analyzerTC := convertWhenToAnalyzerTestCase(windupTestCase.When) + // find where the rule for this test is + foundRule, foundPath := findRuleForWindupTestCase(testsFile, windupTestCase, analyzerTC, windupRulesets) + if foundRule != nil && analyzerTC != nil { + validTests += 1 + analyzerTC.RuleID = foundRule.Id + foundPath = findConvertedPath(foundPath, analyzerRulesets) + _, found := analyzerTestsFile[foundPath] + if !found { + analyzerTestsFilePath := strings.Replace(foundPath, ".yaml", ".test.yaml", 1) + analyzerTestsFilePath = filepath.Join( + filepath.Dir(analyzerTestsFilePath), "tests", filepath.Base(analyzerTestsFilePath)) + analyzerTestDataPath := filepath.Join( + filepath.Dir(analyzerTestsFilePath), "data", + strings.ReplaceAll(filepath.Base(analyzerTestsFilePath), ".windup.test.yaml", "")) + analyzerTestDataRelativePath := strings.ReplaceAll(analyzerTestDataPath, filepath.Dir(analyzerTestsFilePath), ".") + analyzerTestsFile[foundPath] = &testing.TestsFile{ + Path: analyzerTestsFilePath, + Providers: []testing.ProviderConfig{ + {Name: "java", DataPath: analyzerTestDataRelativePath}, + {Name: "builtin", DataPath: analyzerTestDataRelativePath}, + }, + RulesPath: filepath.Join("..", filepath.Base(foundPath)), + Tests: []testing.Test{{ + RuleID: foundRule.Id, + TestCases: []testing.TestCase{}, + }}, + } + + err := os.MkdirAll(filepath.Dir(analyzerTestDataPath), 0755) + if err != nil { + fmt.Printf("failed ceating directories for test data in %s\n", filepath.Dir(analyzerTestsFilePath)) + continue + } + err = copyTestData(testsFile.TestDataPath, analyzerTestDataPath) + if err != nil { + fmt.Printf("failed copying test data from %s to %s - %v\n", testsFile.TestDataPath, analyzerTestDataPath, err) + continue + } + } + var existingTest *testing.Test + for idx := range analyzerTestsFile[foundPath].Tests { + analyzerTest := &analyzerTestsFile[foundPath].Tests[idx] + if analyzerTest.RuleID == foundRule.Id { + existingTest = analyzerTest + analyzerTC.Name = fmt.Sprintf("tc-%d", len(existingTest.TestCases)+1) + existingTest.TestCases = append(existingTest.TestCases, *analyzerTC) + break + } + } + if existingTest == nil { + analyzerTC.Name = "tc-1" + existingTest = &testing.Test{ + RuleID: foundRule.Id, + TestCases: []testing.TestCase{*analyzerTC}, + } + analyzerTestsFile[foundPath].Tests = append(analyzerTestsFile[foundPath].Tests, *existingTest) + } + } else { + fmt.Printf("couldn't convert test %s\n", windupTestCase.Id) + } + } + } + } + + for _, testsFile := range analyzerTestsFile { + content, err := yaml.Marshal(testsFile) + if err != nil { + fmt.Printf("failed marshaling tests file %s\n", testsFile.Path) + continue + } + err = os.WriteFile(testsFile.Path, content, 0644) + if err != nil { + fmt.Printf("failed writing test to file %s\n", testsFile.Path) + continue + } + } + + fmt.Printf("total files %d, total tcs %d, valid %d\n", len(analyzerTests), totalTestsFound, validTests) + return nil +} + +func convertWhenToAnalyzerTestCase(test windup.When) *testing.TestCase { + clean := func(m string) string { + m = regexp.MustCompile(`[^\.]\*$`).ReplaceAllString(m, ".*") + m = regexp.MustCompile(`[$^\\]`).ReplaceAllString(m, "") + m = strings.Replace(m, "\n", "", -1) + m = regexp.MustCompile(`\s{2,}`).ReplaceAllString(m, " *") + m = strings.TrimSpace(m) + return m + } + switch { + case test.Not != nil && len(test.Not) > 0: + return convertWhenToAnalyzerTestCase(test.Not[0]) + case test.And != nil && len(test.And) > 0: + return convertWhenToAnalyzerTestCase(test.And[0]) + case test.Or != nil && len(test.Or) > 0: + return convertWhenToAnalyzerTestCase(test.Or[0]) + case test.Classificationexists != nil && len(test.Classificationexists) > 0: + return &testing.TestCase{ + HasTags: []string{clean(test.Classificationexists[0].Classification)}, + } + case test.Technologystatisticsexists != nil && len(test.Technologystatisticsexists) > 0: + return &testing.TestCase{ + HasTags: []string{clean(test.Technologystatisticsexists[0].Name)}, + } + case test.Technologytagexists != nil && len(test.Technologytagexists) > 0: + return &testing.TestCase{ + HasTags: []string{clean(test.Technologytagexists[0].Technologytag)}, + } + case test.Tofilemodel != nil && len(test.Tofilemodel) > 0: + tf := test.Tofilemodel[0] + return convertWhenToAnalyzerTestCase(windup.When{ + Classificationexists: tf.Classificationexists, + Hintexists: tf.Hintexists, + Lineitemexists: tf.Lineitemexists, + Technologystatisticsexists: tf.Technologystatisticsexists, + Technologytagexists: tf.Technologytagexists, + }) + case test.Iterablefilter != nil && len(test.Iterablefilter) > 0: + it := test.Iterablefilter[0] + tc := convertWhenToAnalyzerTestCase(windup.When{ + Classificationexists: it.Classificationexists, + Technologystatisticsexists: it.Technologystatisticsexists, + Technologytagexists: it.Technologytagexists, + Hintexists: it.Hintexists, + Lineitemexists: it.Lineitemexists, + Tofilemodel: it.Tofilemodel, + }) + if tc != nil && tc.HasIncidents != nil && tc.HasIncidents.CountBased != nil { + tc.HasIncidents.CountBased.AtLeast = &it.Size + } + return tc + case test.Hintexists != nil && len(test.Hintexists) > 0: + msg := clean(test.Hintexists[0].Message) + one := int(1) + return &testing.TestCase{ + HasIncidents: &testing.IncidentVerification{ + CountBased: &testing.CountBasedVerification{ + MessageMatches: &msg, + AtLeast: &one, + }, + }, + } + case test.Lineitemexists != nil && len(test.Lineitemexists) > 0: + msg := clean(test.Lineitemexists[0].Message) + one := int(1) + return &testing.TestCase{ + HasIncidents: &testing.IncidentVerification{ + CountBased: &testing.CountBasedVerification{ + MessageMatches: &msg, + AtLeast: &one, + }, + }, + } + default: + return nil + } +} + +// getMessageAndTagsFromPerform given a perform, return all messages or tags perform creates +func getMessageAndTagsFromPerform(perform windup.Iteration, where []windup.Where) []string { + clean := func(m string) string { + return strings.ReplaceAll(strings.Trim(strings.TrimSpace(m), "*"), "\\", "") + } + switch { + case perform.Hint != nil && len(perform.Hint) > 0: + hint := perform.Hint[0] + msg := clean(hint.Message) + strs := []string{ + clean(hint.Title), + msg, + } + for _, groups := range regexp.MustCompile(`{[A-Za-z0-9]+}`).FindAllStringSubmatch(msg, -1) { + for _, group := range groups { + for _, cond := range where { + v := strings.TrimPrefix(strings.TrimSuffix(group, "}"), "{") + if strings.Contains(cond.Param, v) && len(cond.Matches) > 0 { + for _, possibleVal := range strings.Split(cond.Matches[0].Pattern, "|") { + possibleVal = strings.TrimPrefix(possibleVal, "(") + possibleVal = strings.TrimSuffix(possibleVal, ")") + strs = append(strs, strings.ReplaceAll(msg, group, possibleVal)) + } + } + } + } + } + return strs + case perform.Iteration != nil && len(perform.Iteration) > 0: + strs := []string{} + for _, it := range perform.Iteration { + strs = append(strs, getMessageAndTagsFromPerform(it, where)...) + } + return strs + case perform.Classification != nil && len(perform.Classification) > 0: + strs := []string{} + for _, cl := range perform.Classification { + strs = append(strs, clean(cl.Title)) + for _, t := range cl.Tag { + strs = append(strs, clean(t)) + } + } + return strs + case perform.Technologytag != nil && len(perform.Technologytag) > 0: + strs := []string{} + for _, tt := range perform.Technologytag { + strs = append(strs, clean(tt.Value)) + } + return strs + case perform.Technologyidentified != nil && len(perform.Technologyidentified) > 0: + strs := []string{} + for _, cl := range perform.Technologyidentified { + strs = append(strs, clean(cl.Name)) + for _, t := range cl.Tag { + strs = append(strs, clean(t.Name)) + } + } + return strs + case perform.Perform != nil: + return getMessageAndTagsFromPerform(*perform.Perform, where) + default: + return []string{} + } +} + +// findRuleForWindupTestCase given a test from windup, find its associated rule in test paths +// to find a rule - first, we try to find the rule by ID, but windup tests don't necessarily +// share the rule ID. so second, we try to compare perform field of the rule. +func findRuleForWindupTestCase(testsFile windup.Ruletest, windupTestCase windup.Rule, analyzerTC *testing.TestCase, windupRulesets []windup.Ruleset) (*windup.Rule, string) { + for _, path := range testsFile.RulePath { + for _, windupRuleset := range windupRulesets { + // check if the tests file points to this rules path + stat, err := os.Stat(path) + if err == nil { + if stat.IsDir() && !strings.Contains(windupRuleset.SourceFile, + strings.Replace(filepath.Base(testsFile.SourceFile), "test.xml", "xml", 1)) { + continue + } + if !stat.IsDir() && !strings.Contains(windupRuleset.SourceFile, filepath.Base(path)) { + continue + } + } + seenRules := map[string]*windup.Rule{} + for idx := range windupRuleset.Rules.Rule { + rule := &windupRuleset.Rules.Rule[idx] + seenRules[rule.Id] = rule + // first, try to find rule in the file by id + if strings.Contains(rule.Id, strings.Replace(windupTestCase.Id, "-test", "", 1)) || + strings.Contains(rule.Id, strings.Replace(windupTestCase.Id, "-tests", "", 1)) { + return rule, windupRuleset.SourceFile + } + // second, try to find rule by comparing message & tag fields + messageOrTagStrings := getMessageAndTagsFromPerform(rule.Perform, rule.Where) + if analyzerTC != nil { + if analyzerTC.HasIncidents != nil && + analyzerTC.HasIncidents.CountBased.MessageMatches != nil { + for _, messageStr := range messageOrTagStrings { + msg := *analyzerTC.HasIncidents.CountBased.MessageMatches + // if there are more than one templates in the string, only + // compare until the first template as we can't accurately permute all values + noOfTemplates := strings.Count(messageStr, "{") + if noOfTemplates > 1 { + secondTemplateIdx := strings.Index(strings.Replace(messageStr, "{", ".", 1), "{") + messageStr = messageStr[:secondTemplateIdx] + msg = msg[:int( + math.Min(float64(secondTemplateIdx), float64(len(msg))))] + } + if r, err := regexp.Compile(msg); err == nil && + r.MatchString(messageStr) { + return rule, windupRuleset.SourceFile + } + if strings.Contains(messageStr, msg) { + return rule, windupRuleset.SourceFile + } + } + } + for _, hasTag := range analyzerTC.HasTags { + // check if the rule is creating this tag + for _, messageStr := range messageOrTagStrings { + if messageStr == hasTag { + return rule, windupRuleset.SourceFile + } + } + // if an exact tag is not found, check for the pattern + for _, messageStr := range messageOrTagStrings { + if r, err := regexp.Compile(hasTag); err == nil && + r.MatchString(messageStr) { + return rule, windupRuleset.SourceFile + } + } + } + } + } + // finally, try to find the rule by id with more complex pattern + if val, ok := seenRules[regexp.MustCompile(`-test-[\da-z-]+$`).ReplaceAllString(windupTestCase.Id, "")]; ok { + return val, windupRuleset.SourceFile + } + } + } + return nil, "" +} + +// copyTestData copies test data from windup tests to converted tests +func copyTestData(src string, dest string) error { + if strings.HasSuffix(src, ".jar") { + return exec.Command("cp", "-r", src, dest).Run() + } + // create a dummy java project + appDir := filepath.Join(dest, "src", "main", "java", "com", "example", "apps") + err := os.MkdirAll(appDir, 0755) + if err != nil { + return fmt.Errorf("failed creating java project dirs in %s - %w", dest, err) + } + cmd := exec.Command("find", src, "-type", "f", "-name", "*.java") + out, err := cmd.Output() + if err != nil { + return fmt.Errorf("failed finding java files in %s - %w", src, err) + } + for _, file := range strings.Split(string(out), "\n") { + if file != "" { + err = exec.Command("cp", file, appDir).Run() + if err != nil { + fmt.Printf("failed copying java file %s to %s - %s\n", file, appDir, err.Error()) + continue + } + } + } + cmd = exec.Command("find", ".", "-type", "f", "-not", "-name", "*.java") + cmd.Dir = src + out, err = cmd.Output() + if err != nil { + return fmt.Errorf("failed finding java files in %s - %w", src, err) + } + for _, file := range strings.Split(string(out), "\n") { + if file != "" { + // maintain original directory structure of non java files + cmd := exec.Command("cp", "--parent", file, dest) + cmd.Dir = src + err = cmd.Run() + if err != nil { + fmt.Printf("failed copying file from %s to %s - %s\n", file, appDir, err.Error()) + continue + } + } + } + // if we find a pom.xml file, copy it to the project base + if _, err = os.Stat(filepath.Join(src, "pom.xml")); err == nil { + return exec.Command("cp", filepath.Join(src, "pom.xml"), dest).Run() + } else { + err = os.WriteFile(filepath.Join(dest, "pom.xml"), []byte(` + + 4.0.0 + org.sample + sample-project + 0.0.1 + Sample Project + + `), 0644) + if err != nil { + return err + } + } + return nil +} + func writeRuleset(path string, r *analyzerRules, flattenRulesets bool) error { rulesetGoldenFilePath := filepath.Join(filepath.Dir(path), "ruleset.yaml") // generate labels for the new ruleset we want to create