Convert RESTler bug buckets to Postman collections (#168)
Co-authored-by: stas <statis@microsoft.com>
This commit is contained in:
Родитель
b5d1ff4c07
Коммит
6336c84ff5
|
@ -37,6 +37,12 @@ stages:
|
|||
vmImage: 'ubuntu-latest'
|
||||
steps:
|
||||
- template: steps/result-analyzer.yml
|
||||
|
||||
- job: RESTler2Postman
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
steps:
|
||||
- template: steps/restler2postman.yml
|
||||
|
||||
- job: AzureAuthUtility
|
||||
pool:
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
steps:
|
||||
- task: NuGetToolInstaller@1
|
||||
displayName: 'Use NuGet 5.8'
|
||||
inputs:
|
||||
versionSpec: 5.8
|
||||
|
||||
- task: NuGetCommand@2
|
||||
displayName: 'NuGet restore'
|
||||
inputs:
|
||||
restoreSolution: '**\RESTlerAgent.sln'
|
||||
|
||||
- task: DotNetCoreCLI@2
|
||||
displayName: 'RESTler To Postman'
|
||||
inputs:
|
||||
command: publish
|
||||
publishWebProjects: false
|
||||
projects: src/Agent/RESTler2Postman/RESTler2Postman.fsproj
|
||||
arguments: '-c release /p:version=$(versionNumber)'
|
||||
zipAfterPublish: false
|
||||
|
||||
- task: PublishPipelineArtifact@1
|
||||
inputs:
|
||||
targetPath: src/Agent/RESTler2Postman/bin/release/net5.0/publish/
|
||||
artifactName: RESTler2Postman
|
|
@ -16,6 +16,12 @@ steps:
|
|||
artifact: RaftResultAnalyzer
|
||||
path: $(Build.SourcesDirectory)/artifacts/RaftResultAnalyzer
|
||||
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download Local pipeline artifact RESTler2Postman'
|
||||
inputs:
|
||||
artifact: RESTler2Postman
|
||||
path: $(Build.SourcesDirectory)/artifacts/RESTler2Postman
|
||||
|
||||
- ${{ if eq(parameters.BuildArtifactsLocation, 'production') }}:
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download Production pipeline artifact RestlerAgent'
|
||||
|
@ -40,6 +46,17 @@ steps:
|
|||
path: $(Build.SourcesDirectory)/artifacts/RaftResultAnalyzer
|
||||
runVersion: 'latest'
|
||||
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download Production pipeline artifact RESTler2Postman'
|
||||
inputs:
|
||||
source: 'specific'
|
||||
project: 'raft'
|
||||
pipeline: $(build-production-pipeline-id)
|
||||
artifact: RESTler2Postman
|
||||
path: $(Build.SourcesDirectory)/artifacts/RESTler2Postman
|
||||
runVersion: 'latest'
|
||||
|
||||
|
||||
# This authenticates against the service connection which is needed to pull the restler image
|
||||
# from the repository.
|
||||
- task: Docker@2
|
||||
|
|
|
@ -6,14 +6,39 @@
|
|||
|
||||
"testTasks": {
|
||||
"targetConfiguration" : {
|
||||
"endpoint" : "https://petstore.swagger.io",
|
||||
"apiSpecifications": [
|
||||
"https://petstore.swagger.io/v2/swagger.json"
|
||||
]
|
||||
},
|
||||
"tasks": [
|
||||
{
|
||||
"targetConfiguration" : {
|
||||
"endpoint" : "https://petstore3.swagger.io",
|
||||
"apiSpecifications": [
|
||||
"https://petstore3.swagger.io/api/v3/openapi.json"
|
||||
]
|
||||
},
|
||||
"toolName": "RESTler",
|
||||
"outputFolder": "restler-run",
|
||||
"outputFolder": "restler-pestorev3",
|
||||
"toolConfiguration": {
|
||||
"tasks": [
|
||||
{"task": "Compile"},
|
||||
|
||||
{"task" : "Test"},
|
||||
|
||||
{
|
||||
"task" : "Fuzz",
|
||||
"runConfiguration" : {
|
||||
"Duration" : "00:10:00"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"toolName": "RESTler",
|
||||
"outputFolder": "restler-petstorev2",
|
||||
"toolConfiguration": {
|
||||
"tasks": [
|
||||
{"task": "Compile"},
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
FROM mcr.microsoft.com/restlerfuzzer/restler:v7.3.0
|
||||
COPY RestlerAgent /raft/agent
|
||||
COPY RaftResultAnalyzer /raft/result-analyzer
|
||||
COPY RESTler2Postman /raft/restler2postman
|
|
@ -0,0 +1,407 @@
|
|||
// Learn more about F# at http://docs.microsoft.com/dotnet/fsharp
|
||||
|
||||
open System
|
||||
open Microsoft.FSharpLu
|
||||
open Newtonsoft
|
||||
|
||||
type RESTlerBugFilePosition =
|
||||
| Intro
|
||||
| Request
|
||||
|
||||
type RestlerRequestDetails =
|
||||
{
|
||||
producerTimingDelay : int option
|
||||
maxAsyncWaitTime : int option
|
||||
|
||||
previousResponse : string option
|
||||
}
|
||||
|
||||
static member Empty =
|
||||
{
|
||||
producerTimingDelay = None
|
||||
maxAsyncWaitTime = None
|
||||
|
||||
previousResponse = None
|
||||
}
|
||||
|
||||
|
||||
|
||||
type RestlerRequest =
|
||||
{
|
||||
httpMethod : string
|
||||
query :string
|
||||
headers : Map<string, string>
|
||||
body: string option
|
||||
|
||||
restlerRequestDetails : RestlerRequestDetails
|
||||
}
|
||||
|
||||
|
||||
type RESTlerBug =
|
||||
{
|
||||
bugName : string option
|
||||
bugHash: string option
|
||||
|
||||
requests : RestlerRequest list
|
||||
}
|
||||
|
||||
static member Empty =
|
||||
{
|
||||
bugName = None
|
||||
bugHash = None
|
||||
requests = []
|
||||
}
|
||||
|
||||
module Constants =
|
||||
let [<Literal>] IntroSeparator = "################################################################################"
|
||||
let [<Literal>] Hash = "Hash:"
|
||||
let [<Literal>] Request = "->"
|
||||
let [<Literal>] PreviousResponse = "PREVIOUS RESPONSE:"
|
||||
let [<Literal>] Host = "Host:"
|
||||
let [<Literal>] Accept = "Accept:"
|
||||
let [<Literal>] ContentType = "Content-Type:"
|
||||
|
||||
module RequestConfig =
|
||||
let [<Literal>] ProducerTimerDelay = "! producer_timing_delay"
|
||||
let [<Literal>] MaxAsyncWaitTime = "! max_async_wait_time"
|
||||
|
||||
|
||||
|
||||
|
||||
let parseRequest (x: string) =
|
||||
let r = x.Substring(Constants.Request.Length).Split("\\r\\n") |> List.ofArray
|
||||
let method, query =
|
||||
match ((List.head r).Trim().Split(' ')) |> List.ofArray with
|
||||
| m :: q :: _ -> m.Trim(), q.Trim()
|
||||
| _ -> failwithf "Unhandled RESTler log format (expected HTTP method followed by query): %s" (r.[0])
|
||||
|
||||
//from r.[1] to end, but if "" line and not last one -> then next one is body
|
||||
let rec collectHeaders (headers: Map<string, string>) (rs: string list) =
|
||||
let parseHeader (h: string) =
|
||||
match h.Split(':') with
|
||||
| [|k; v|] -> Some(k.Trim(), v.Trim())
|
||||
| _ -> None
|
||||
|
||||
match rs with
|
||||
| "" :: body :: "" :: [] -> headers, Some body
|
||||
| r :: "" :: [] ->
|
||||
match parseHeader r with
|
||||
| Some(k, v) ->
|
||||
(Map.add k v headers), None
|
||||
| None -> headers, None
|
||||
| r :: rs ->
|
||||
match parseHeader r with
|
||||
| Some(k, v) -> collectHeaders (Map.add k v headers) rs
|
||||
| None -> collectHeaders headers rs
|
||||
| [] -> headers, None
|
||||
|
||||
let headers, body = collectHeaders Map.empty (List.tail r)
|
||||
let body =
|
||||
match body with
|
||||
| Some s ->
|
||||
let b = Json.Compact.deserialize(s.Replace("\\n", ""))
|
||||
Some(b.ToString())
|
||||
| None -> None
|
||||
|
||||
{|
|
||||
HttpMethod = method
|
||||
Query = query
|
||||
Headers= headers
|
||||
Body = body
|
||||
|}
|
||||
|
||||
|
||||
|
||||
let parseRESTlerBugFound(bugFound: string list) =
|
||||
|
||||
let rec parse (xs: string list) (bugDefinition : RESTlerBug) (pos: RESTlerBugFilePosition) =
|
||||
match pos, xs with
|
||||
| Intro, (Constants.IntroSeparator::[]) -> None
|
||||
|
||||
| Intro, (Constants.IntroSeparator::y::xs) when not (String.IsNullOrWhiteSpace y) ->
|
||||
parse xs {bugDefinition with bugName = Some y} Intro
|
||||
|
||||
| Intro, x::xs when x.Trim().StartsWith(Constants.Hash) ->
|
||||
parse xs {bugDefinition with bugHash = Some (x.Trim().Substring(Constants.Hash.Length)) } Intro
|
||||
|
||||
| Intro, (Constants.IntroSeparator::y::xs) when String.IsNullOrWhiteSpace y ->
|
||||
parse xs bugDefinition Request
|
||||
|
||||
// Skip rest of bug definition intro
|
||||
| Intro, x::xs ->
|
||||
if not (String.IsNullOrWhiteSpace x) then
|
||||
printfn "Ignoring: %s" x
|
||||
parse xs bugDefinition Intro
|
||||
|
||||
| Request, (x :: xs) when String.IsNullOrEmpty x -> parse xs bugDefinition Request
|
||||
|
||||
| Request, (x :: xs) when x.StartsWith Constants.Request ->
|
||||
let r = parseRequest x
|
||||
|
||||
let rec restlerRequestDetails (xs: string list) (requestDetails: RestlerRequestDetails) =
|
||||
match xs with
|
||||
| y :: ys when y.StartsWith(Constants.RequestConfig.MaxAsyncWaitTime) ->
|
||||
let t = y.Substring(Constants.RequestConfig.MaxAsyncWaitTime.Length)
|
||||
match Int32.TryParse t with
|
||||
| true, n -> restlerRequestDetails ys {requestDetails with maxAsyncWaitTime = Some n}
|
||||
| false, _ -> failwithf "Expected max async wait time to be an integer: %s" y
|
||||
|
||||
| y :: ys when y.StartsWith(Constants.RequestConfig.ProducerTimerDelay) ->
|
||||
let t = y.Substring(Constants.RequestConfig.ProducerTimerDelay.Length)
|
||||
match Int32.TryParse t with
|
||||
| true, n -> restlerRequestDetails ys {requestDetails with producerTimingDelay = Some n}
|
||||
| false, _ -> failwithf "Expected producer timing delay to be an integer: %s" y
|
||||
|
||||
| y :: ys when y.StartsWith(Constants.PreviousResponse) ->
|
||||
{requestDetails with previousResponse = Some (y.Substring(Constants.PreviousResponse.Length))}, ys
|
||||
|
||||
| s :: _ -> failwithf "Unhandled RESTler request details : %s" s
|
||||
| ss -> failwithf "Unhandled case when processing RESTler request details : %A" ss
|
||||
|
||||
|
||||
let requestDetails, rest = restlerRequestDetails xs RestlerRequestDetails.Empty
|
||||
|
||||
let request: RestlerRequest =
|
||||
{
|
||||
httpMethod = r.HttpMethod
|
||||
query = r.Query
|
||||
headers = r.Headers
|
||||
body = r.Body
|
||||
restlerRequestDetails = requestDetails
|
||||
}
|
||||
|
||||
parse rest { bugDefinition with requests = bugDefinition.requests @ [request] } Request
|
||||
|
||||
| _ -> Some bugDefinition
|
||||
|
||||
parse (bugFound |> Seq.toList) RESTlerBug.Empty Intro
|
||||
|
||||
|
||||
|
||||
|
||||
module Postman =
|
||||
type Name = Json.JsonPropertyAttribute
|
||||
|
||||
type Info =
|
||||
{
|
||||
[<Name("_postman_id")>]
|
||||
PostmanId: System.Guid
|
||||
[<Name("name")>]
|
||||
Name: string
|
||||
[<Name("schema")>]
|
||||
Schema: string
|
||||
}
|
||||
|
||||
static member Create(name: string) =
|
||||
{
|
||||
PostmanId = System.Guid.NewGuid()
|
||||
Name = name
|
||||
Schema = "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
||||
}
|
||||
|
||||
|
||||
type Query =
|
||||
{
|
||||
[<Name("key")>]
|
||||
Key : string
|
||||
[<Name("value")>]
|
||||
Value : string
|
||||
}
|
||||
|
||||
|
||||
type Url =
|
||||
{
|
||||
[<Name("raw")>]
|
||||
Raw : string
|
||||
[<Name("protocol")>]
|
||||
Protocol: string
|
||||
[<Name("host")>]
|
||||
Host : string array
|
||||
[<Name("path")>]
|
||||
Path : string array
|
||||
[<Name("query")>]
|
||||
Query : Collections.Specialized.NameValueCollection option
|
||||
}
|
||||
|
||||
|
||||
|
||||
type BodyLanguage =
|
||||
{
|
||||
[<Name("language")>]
|
||||
Language : string
|
||||
}
|
||||
|
||||
type BodyOptions =
|
||||
{
|
||||
[<Name("raw")>]
|
||||
Raw : BodyLanguage
|
||||
}
|
||||
|
||||
type Body =
|
||||
{
|
||||
[<Name("mode")>]
|
||||
Mode: string
|
||||
[<Name("raw")>]
|
||||
Raw: string
|
||||
[<Name("options")>]
|
||||
Options : BodyOptions
|
||||
}
|
||||
|
||||
type Header =
|
||||
{
|
||||
[<Name("key")>]
|
||||
Key : string
|
||||
[<Name("value")>]
|
||||
Value : string
|
||||
}
|
||||
|
||||
type Request =
|
||||
{
|
||||
[<Name("method")>]
|
||||
Method : string
|
||||
[<Name("header")>]
|
||||
Header : Header array
|
||||
[<Name("body")>]
|
||||
Body : Body option
|
||||
[<Name("url")>]
|
||||
Url : Url
|
||||
}
|
||||
|
||||
type Response =
|
||||
{
|
||||
[<Name("code")>]
|
||||
Code : int
|
||||
}
|
||||
|
||||
type SystemHeaders =
|
||||
{
|
||||
[<Name("accept")>]
|
||||
Accept : bool
|
||||
|
||||
[<Name("content-type")>]
|
||||
ContentType : bool
|
||||
}
|
||||
|
||||
type ProtocolProfileBehaviour =
|
||||
{
|
||||
[<Name("disabledSystemHeaders")>]
|
||||
DisabledSystemHeaders: SystemHeaders
|
||||
}
|
||||
|
||||
|
||||
type Item =
|
||||
{
|
||||
[<Name("name")>]
|
||||
Name : string
|
||||
[<Name("protocolProfileBehavior")>]
|
||||
ProtocolProfileBehavior : ProtocolProfileBehaviour
|
||||
[<Name("request")>]
|
||||
Request : Request
|
||||
[<Name("response")>]
|
||||
Response : Response array
|
||||
}
|
||||
|
||||
|
||||
type Collection =
|
||||
{
|
||||
[<Name("info")>]
|
||||
Info : Info
|
||||
[<Name("item")>]
|
||||
Item : Item list
|
||||
}
|
||||
|
||||
let convertToPostmanFormat (useSsl: bool) (name: string) (bug : RESTlerBug) =
|
||||
let info = Info.Create(name)
|
||||
let requests =
|
||||
bug.requests
|
||||
|> List.map(fun r ->
|
||||
let host = r.headers.["Host"]
|
||||
let headers = r.headers.Remove("Host")
|
||||
{
|
||||
Name = sprintf "%s:%s" r.httpMethod (r.query.Substring(0, (min 24 r.query.Length)))
|
||||
ProtocolProfileBehavior = {DisabledSystemHeaders = {Accept = true; ContentType = true}}
|
||||
Request =
|
||||
{
|
||||
Method = r.httpMethod
|
||||
Header =
|
||||
headers
|
||||
|> Map.toArray
|
||||
|> Array.map (fun (k, v) -> {Key = k; Value = v})
|
||||
|
||||
Body =
|
||||
r.body |> Option.map(fun b ->
|
||||
{
|
||||
Mode = "raw"
|
||||
Raw = b
|
||||
Options = {Raw = {Language = "json"}}
|
||||
}
|
||||
)
|
||||
|
||||
Url =
|
||||
let protocol = if useSsl then "https" else "http"
|
||||
let uri = System.Uri(sprintf "%s://%s%s" protocol host r.query)
|
||||
let parsedQuery = System.Web.HttpUtility.ParseQueryString(uri.Query)
|
||||
{
|
||||
Raw = uri.AbsoluteUri
|
||||
Protocol = protocol
|
||||
Host = host.Split('.')
|
||||
Path = uri.AbsolutePath.Split('/') |> Array.filter(fun s -> not <| String.IsNullOrEmpty s)
|
||||
Query =
|
||||
if parsedQuery.Count = 0 then
|
||||
None
|
||||
else
|
||||
Some parsedQuery
|
||||
}
|
||||
}
|
||||
Response = [||]
|
||||
}
|
||||
)
|
||||
|
||||
{
|
||||
Info = info
|
||||
Item = requests
|
||||
}
|
||||
|
||||
|
||||
module CommandLine =
|
||||
let [<Literal>] UseSSL = "--use-ssl"
|
||||
let [<Literal>] RESTlerBugBucket = "--restler-bug-bucket-path"
|
||||
|
||||
let parse (args: string array) =
|
||||
let rec parse (r : {| UseSsl : bool; RESTlerBugBucketPath : string option |}) (xs : string list) =
|
||||
match xs with
|
||||
| [] -> r
|
||||
| UseSSL :: xs ->
|
||||
parse {| r with UseSsl = true |} xs
|
||||
| RESTlerBugBucket :: p :: xs ->
|
||||
parse {| r with RESTlerBugBucketPath = Some p |} xs
|
||||
| s -> failwithf "Unhandled command line parameters: %A" s
|
||||
|
||||
let r = parse {| UseSsl = false; RESTlerBugBucketPath = None |} (List.ofArray args)
|
||||
match r.RESTlerBugBucketPath with
|
||||
| None ->
|
||||
failwithf "Expected RESTler bug bucket as an input. Parameters: %s %s [path]" UseSSL RESTlerBugBucket
|
||||
| Some p -> {| UseSSL = r.UseSsl; RESTlerBugBucketPath = p |}
|
||||
|
||||
|
||||
[<EntryPoint>]
|
||||
let main argv =
|
||||
let config = CommandLine.parse argv
|
||||
let fileContents =
|
||||
use stream = System.IO.File.OpenText(config.RESTlerBugBucketPath)
|
||||
[
|
||||
while not stream.EndOfStream do
|
||||
yield stream.ReadLine().Trim()
|
||||
]
|
||||
|
||||
match parseRESTlerBugFound fileContents with
|
||||
| None ->
|
||||
printfn "Failed to parse RESTler bug information from : %s" config.RESTlerBugBucketPath
|
||||
1
|
||||
| Some bug ->
|
||||
let bugFileName = System.IO.Path.GetFileNameWithoutExtension(config.RESTlerBugBucketPath)
|
||||
let p = System.IO.Path.ChangeExtension(config.RESTlerBugBucketPath, ".postman.json")
|
||||
let postmanCollection = Postman.convertToPostmanFormat config.UseSSL (sprintf "%s [%s]" bugFileName bug.bugHash.Value) bug
|
||||
printfn "Writing Postman collection to: %s" p
|
||||
Json.Compact.serializeToFile p postmanCollection
|
||||
0 // return an integer exit code
|
|
@ -0,0 +1,20 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.fs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.FSharpLu.Json" Version="0.11.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="FSharp.Core" Version="5.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -7,7 +7,9 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "RestlerAgent", "RESTlerAgen
|
|||
EndProject
|
||||
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "RaftResultAnalyzer", "RaftResultAnalyzer\RaftResultAnalyzer.fsproj", "{32475F74-7C03-4547-ACF2-C02B1BF6F85A}"
|
||||
EndProject
|
||||
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "AzureAuth", "AzureAuth\AzureAuth.fsproj", "{8A202DC0-DBEC-425C-8504-BCF3FDD1AE4B}"
|
||||
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "AzureAuth", "AzureAuth\AzureAuth.fsproj", "{8A202DC0-DBEC-425C-8504-BCF3FDD1AE4B}"
|
||||
EndProject
|
||||
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "RESTler2Postman", "RESTler2Postman\RESTler2Postman.fsproj", "{9483A0A4-0FD0-4905-BDA9-72A8FE70D670}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -27,6 +29,10 @@ Global
|
|||
{8A202DC0-DBEC-425C-8504-BCF3FDD1AE4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8A202DC0-DBEC-425C-8504-BCF3FDD1AE4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8A202DC0-DBEC-425C-8504-BCF3FDD1AE4B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9483A0A4-0FD0-4905-BDA9-72A8FE70D670}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9483A0A4-0FD0-4905-BDA9-72A8FE70D670}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9483A0A4-0FD0-4905-BDA9-72A8FE70D670}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9483A0A4-0FD0-4905-BDA9-72A8FE70D670}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -28,6 +28,8 @@ module private RESTlerInternal =
|
|||
let resultAnalyzer =
|
||||
"/raft" ++ "result-analyzer" ++ "RaftResultAnalyzer.dll"
|
||||
|
||||
let postmanConverter =
|
||||
"/raft" ++ "restler2postman" ++ "RESTler2Postman.dll"
|
||||
|
||||
(*
|
||||
let SupportedCheckers =
|
||||
|
@ -85,24 +87,29 @@ module private RESTlerInternal =
|
|||
// buffer and waits for the parent to consume it, and the parent waits for the
|
||||
// child process to exit first.
|
||||
// Reference: https://stackoverflow.com/questions/139593/processstartinfo-hanging-on-waitforexit-why?lq=1
|
||||
use stdOutFile =
|
||||
match stdOutFilePath with
|
||||
| Some path -> System.IO.File.CreateText(path) :> IO.TextWriter
|
||||
| None -> IO.TextWriter.Null
|
||||
use stdErrFile =
|
||||
match stdErrFilePath with
|
||||
| Some path -> System.IO.File.CreateText(path) :> IO.TextWriter
|
||||
| None -> IO.TextWriter.Null
|
||||
let stdOutFile =
|
||||
lazy(
|
||||
match stdOutFilePath with
|
||||
| Some path -> System.IO.File.CreateText(path) :> IO.TextWriter
|
||||
| None -> IO.TextWriter.Null
|
||||
)
|
||||
|
||||
let stdErrFile =
|
||||
lazy(
|
||||
match stdErrFilePath with
|
||||
| Some path -> System.IO.File.CreateText(path) :> IO.TextWriter
|
||||
| None -> IO.TextWriter.Null
|
||||
)
|
||||
|
||||
let appendHandler
|
||||
(endOfStreamEvent:System.Threading.AutoResetEvent)
|
||||
(aggregator:IO.TextWriter)
|
||||
(aggregator:Lazy<IO.TextWriter>)
|
||||
(dataReceived:Diagnostics.DataReceivedEventArgs) =
|
||||
if isNull dataReceived.Data then
|
||||
if not endOfStreamEvent.SafeWaitHandle.IsClosed && not endOfStreamEvent.SafeWaitHandle.IsInvalid then
|
||||
endOfStreamEvent.Set() |> ignore
|
||||
else
|
||||
aggregator.WriteLine(dataReceived.Data) |> ignore
|
||||
aggregator.Value.WriteLine(dataReceived.Data) |> ignore
|
||||
|
||||
instance.OutputDataReceived.Add(appendHandler noMoreOutput stdOutFile)
|
||||
instance.ErrorDataReceived.Add(appendHandler noMoreError stdErrFile)
|
||||
|
@ -123,17 +130,22 @@ module private RESTlerInternal =
|
|||
None
|
||||
|
||||
try
|
||||
do! stdOutFile.FlushAsync() |> Async.AwaitTask
|
||||
if stdOutFile.IsValueCreated then
|
||||
do! stdOutFile.Value.FlushAsync() |> Async.AwaitTask
|
||||
with ex ->
|
||||
printfn "Failed to flush stdoout due to %A" ex
|
||||
|
||||
try
|
||||
do! stdErrFile.FlushAsync() |> Async.AwaitTask
|
||||
if stdErrFile.IsValueCreated then
|
||||
do! stdErrFile.Value.FlushAsync() |> Async.AwaitTask
|
||||
with ex ->
|
||||
printfn "Failed to flush stderr due to %A" ex
|
||||
|
||||
stdOutFile.Close()
|
||||
stdErrFile.Close()
|
||||
if stdOutFile.IsValueCreated then
|
||||
stdOutFile.Value.Close()
|
||||
|
||||
if stdErrFile.IsValueCreated then
|
||||
stdErrFile.Value.Close()
|
||||
|
||||
return
|
||||
{
|
||||
|
@ -317,6 +329,19 @@ module private RESTlerInternal =
|
|||
}
|
||||
|
||||
|
||||
let convertBugBucketToPostmanCollection (useSsl: bool) (bugFilePath: string) =
|
||||
async {
|
||||
let! result =
|
||||
startProcessAsync
|
||||
Runtime.DotNet
|
||||
(sprintf "\"%s\" %s %s" Paths.postmanConverter (if useSsl then "--use-ssl" else "") (sprintf "--restler-bug-bucket-path \"%s\"" bugFilePath))
|
||||
"."
|
||||
None
|
||||
(Some (sprintf "%s.convert.err.txt" bugFilePath))
|
||||
return ()
|
||||
}
|
||||
|
||||
|
||||
let test testType restlerRootDirectory workingDirectory (parameters: Raft.RESTlerTypes.Engine.EngineParameters) =
|
||||
async {
|
||||
do!
|
||||
|
@ -461,7 +486,9 @@ let getListOfBugs workingDirectory (runStartTime: DateTime) =
|
|||
|
||||
let bugFoundPollInterval = TimeSpan.FromSeconds (10.0)
|
||||
type OnBugFound = Map<string, string> -> Async<unit>
|
||||
let pollForBugFound workingDirectory (token: Threading.CancellationToken) (runStartTime: DateTime) (ignoreBugHashes: string Set) (onBugFound : OnBugFound) =
|
||||
type ConvertBugBucket = string -> Async<unit>
|
||||
|
||||
let pollForBugFound workingDirectory (token: Threading.CancellationToken) (runStartTime: DateTime) (ignoreBugHashes: string Set) (onBugFound : OnBugFound) (convert: ConvertBugBucket) =
|
||||
let rec poll() =
|
||||
async {
|
||||
if token.IsCancellationRequested then
|
||||
|
@ -493,6 +520,7 @@ let pollForBugFound workingDirectory (token: Threading.CancellationToken) (runSt
|
|||
|> Seq.map (fun (KeyValue(bugHash, bugFile)) ->
|
||||
async {
|
||||
if not <| postedBugs.Contains bugHash then
|
||||
do! convert (experiment.FullName ++ "bug_buckets" ++ bugFile.file_path)
|
||||
do! onBugFound (Map.empty.Add("Experiment", experiment.Name).Add("BugBucket", bugFile.file_path).Add("BugHash", bugHash))
|
||||
return bugHash
|
||||
}
|
||||
|
@ -524,7 +552,7 @@ let test (testType: string)
|
|||
token.Cancel()
|
||||
}
|
||||
resultAnalyzer workingDirectory token.Token report (runStartTime, reportInterval)
|
||||
pollForBugFound workingDirectory token.Token runStartTime ignoreBugHashes onBugFound
|
||||
pollForBugFound workingDirectory token.Token runStartTime ignoreBugHashes onBugFound (RESTlerInternal.convertBugBucketToPostmanCollection parameters.UseSsl)
|
||||
]
|
||||
return ()
|
||||
}
|
||||
|
@ -545,7 +573,7 @@ let fuzz (fuzzType: string)
|
|||
token.Cancel()
|
||||
}
|
||||
resultAnalyzer workingDirectory token.Token report (runStartTime, reportInterval)
|
||||
pollForBugFound workingDirectory token.Token runStartTime ignoreBugHashes onBugFound
|
||||
pollForBugFound workingDirectory token.Token runStartTime ignoreBugHashes onBugFound (RESTlerInternal.convertBugBucketToPostmanCollection parameters.UseSsl)
|
||||
]
|
||||
return ()
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Orchestrator", "Orchestrato
|
|||
EndProject
|
||||
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "OrchestratorLogic", "Orchestrator\OrchestratorLogic\OrchestratorLogic.fsproj", "{BFE2B877-4015-4519-92D8-F6A633770B15}"
|
||||
EndProject
|
||||
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "RESTler2Postman", "Agent\RESTler2Postman\RESTler2Postman.fsproj", "{3B83CE52-07BB-4CA6-9AA6-B0962E064330}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -63,6 +65,10 @@ Global
|
|||
{BFE2B877-4015-4519-92D8-F6A633770B15}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BFE2B877-4015-4519-92D8-F6A633770B15}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BFE2B877-4015-4519-92D8-F6A633770B15}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3B83CE52-07BB-4CA6-9AA6-B0962E064330}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3B83CE52-07BB-4CA6-9AA6-B0962E064330}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3B83CE52-07BB-4CA6-9AA6-B0962E064330}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3B83CE52-07BB-4CA6-9AA6-B0962E064330}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
Загрузка…
Ссылка в новой задаче