github.com/simonmittag/ws@v1.1.0-rc.5.0.20210419231947-82b846128245/autobahn/main.go (about)

     1  package main
     2  
     3  import (
     4  	"encoding/json"
     5  	"flag"
     6  	"fmt"
     7  	"html/template"
     8  	"log"
     9  	"net/http"
    10  	"os"
    11  	"path"
    12  	"sort"
    13  	"strconv"
    14  	"strings"
    15  	"text/tabwriter"
    16  )
    17  
    18  var (
    19  	verbose = flag.Bool("verbose", false, "be verbose")
    20  	web     = flag.String("http", "", "open web browser instead")
    21  )
    22  
    23  const (
    24  	statusOK            = "OK"
    25  	statusInformational = "INFORMATIONAL"
    26  	statusUnimplemented = "UNIMPLEMENTED"
    27  	statusNonStrict     = "NON-STRICT"
    28  	statusUnclean       = "UNCLEAN"
    29  	statusFailed        = "FAILED"
    30  )
    31  
    32  func failing(behavior string) bool {
    33  	switch behavior {
    34  	case statusUnclean, statusFailed, statusNonStrict:
    35  		return true
    36  	default:
    37  		return false
    38  	}
    39  }
    40  
    41  type statusCounter struct {
    42  	Total         int
    43  	OK            int
    44  	Informational int
    45  	Unimplemented int
    46  	NonStrict     int
    47  	Unclean       int
    48  	Failed        int
    49  }
    50  
    51  func (c *statusCounter) Inc(s string) {
    52  	c.Total++
    53  	switch s {
    54  	case statusOK:
    55  		c.OK++
    56  	case statusInformational:
    57  		c.Informational++
    58  	case statusNonStrict:
    59  		c.NonStrict++
    60  	case statusUnimplemented:
    61  		c.Unimplemented++
    62  	case statusUnclean:
    63  		c.Unclean++
    64  	case statusFailed:
    65  		c.Failed++
    66  	default:
    67  		panic(fmt.Sprintf("unexpected status %q", s))
    68  	}
    69  }
    70  
    71  func main() {
    72  	log.SetFlags(0)
    73  	flag.Parse()
    74  
    75  	if flag.NArg() < 1 {
    76  		log.Fatalf("Usage: %s [options] <report-path>", os.Args[0])
    77  	}
    78  
    79  	base := path.Dir(flag.Arg(0))
    80  
    81  	if addr := *web; addr != "" {
    82  		http.HandleFunc("/", handlerIndex())
    83  		http.Handle("/report/", http.StripPrefix("/report/",
    84  			http.FileServer(http.Dir(base)),
    85  		))
    86  		log.Fatal(http.ListenAndServe(addr, nil))
    87  		return
    88  	}
    89  
    90  	var report report
    91  	if err := decodeFile(os.Args[1], &report); err != nil {
    92  		log.Fatal(err)
    93  	}
    94  
    95  	var servers []string
    96  	for s := range report {
    97  		servers = append(servers, s)
    98  	}
    99  	sort.Strings(servers)
   100  
   101  	var (
   102  		failed bool
   103  	)
   104  	tw := tabwriter.NewWriter(os.Stderr, 0, 4, 1, ' ', 0)
   105  	for _, server := range servers {
   106  		var (
   107  			srvFailed  bool
   108  			hdrWritten bool
   109  			counter    statusCounter
   110  		)
   111  
   112  		var cases []string
   113  		for id := range report[server] {
   114  			cases = append(cases, id)
   115  		}
   116  		sortBySegment(cases)
   117  		for _, id := range cases {
   118  			c := report[server][id]
   119  
   120  			var r entryReport
   121  			err := decodeFile(path.Join(base, c.ReportFile), &r)
   122  			if err != nil {
   123  				log.Fatal(err)
   124  			}
   125  			counter.Inc(c.Behavior)
   126  			bad := failing(c.Behavior)
   127  			if bad {
   128  				srvFailed = true
   129  				failed = true
   130  			}
   131  			if *verbose || bad {
   132  				if !hdrWritten {
   133  					hdrWritten = true
   134  					n, _ := fmt.Fprintf(os.Stderr, "AGENT %q\n", server)
   135  					fmt.Fprintf(tw, "%s\n", strings.Repeat("=", n-1))
   136  				}
   137  				fmt.Fprintf(tw, "%s\t%s\t%s\n", server, id, c.Behavior)
   138  			}
   139  			if bad {
   140  				fmt.Fprintf(tw, "\tdesc:\t%s\n", r.Description)
   141  				fmt.Fprintf(tw, "\texp: \t%s\n", r.Expectation)
   142  				fmt.Fprintf(tw, "\tact: \t%s\n", r.Result)
   143  			}
   144  		}
   145  		if hdrWritten {
   146  			fmt.Fprint(tw, "\n")
   147  		}
   148  		var status string
   149  		if srvFailed {
   150  			status = statusFailed
   151  		} else {
   152  			status = statusOK
   153  		}
   154  		n, _ := fmt.Fprintf(tw, "AGENT %q SUMMARY (%s)\n", server, status)
   155  		fmt.Fprintf(tw, "%s\n", strings.Repeat("=", n-1))
   156  
   157  		fmt.Fprintf(tw, "TOTAL:\t%d\n", counter.Total)
   158  		fmt.Fprintf(tw, "%s:\t%d\n", statusOK, counter.OK)
   159  		fmt.Fprintf(tw, "%s:\t%d\n", statusInformational, counter.Informational)
   160  		fmt.Fprintf(tw, "%s:\t%d\n", statusUnimplemented, counter.Unimplemented)
   161  		fmt.Fprintf(tw, "%s:\t%d\n", statusNonStrict, counter.NonStrict)
   162  		fmt.Fprintf(tw, "%s:\t%d\n", statusUnclean, counter.Unclean)
   163  		fmt.Fprintf(tw, "%s:\t%d\n", statusFailed, counter.Failed)
   164  		fmt.Fprint(tw, "\n")
   165  		tw.Flush()
   166  	}
   167  	var rc int
   168  	if failed {
   169  		rc = 1
   170  		fmt.Fprintf(tw, "\n\nTEST %s\n\n", statusFailed)
   171  	} else {
   172  		fmt.Fprintf(tw, "\n\nTEST %s\n\n", statusOK)
   173  	}
   174  
   175  	tw.Flush()
   176  	os.Exit(rc)
   177  }
   178  
   179  type spec struct {
   180  	OutDir string `json:"outdir"`
   181  }
   182  
   183  type report map[string]server
   184  
   185  type server map[string]entry
   186  
   187  type entry struct {
   188  	Behavior        string `json:"behavior"`
   189  	BehaviorClose   string `json:"behaviorClose"`
   190  	Duration        int    `json:"duration"`
   191  	RemoveCloseCode int    `json:"removeCloseCode"`
   192  	ReportFile      string `json:"reportFile"`
   193  }
   194  
   195  type entryReport struct {
   196  	Description string `json:"description"`
   197  	Expectation string `json:"expectation"`
   198  	Result      string `json:"result"`
   199  	Duration    int    `json:"duration"`
   200  }
   201  
   202  func decodeFile(path string, x interface{}) error {
   203  	f, err := os.Open(path)
   204  	if err != nil {
   205  		return err
   206  	}
   207  	defer f.Close()
   208  
   209  	d := json.NewDecoder(f)
   210  	return d.Decode(x)
   211  }
   212  
   213  func compareBySegment(a, b string) int {
   214  	as := strings.Split(a, ".")
   215  	bs := strings.Split(b, ".")
   216  	for i := 0; i < min(len(as), len(bs)); i++ {
   217  		ax := mustInt(as[i])
   218  		bx := mustInt(bs[i])
   219  		if ax == bx {
   220  			continue
   221  		}
   222  		return ax - bx
   223  	}
   224  	return len(b) - len(a)
   225  }
   226  
   227  func mustInt(s string) int {
   228  	const bits = 32 << (^uint(0) >> 63)
   229  	x, err := strconv.ParseInt(s, 10, bits)
   230  	if err != nil {
   231  		panic(err)
   232  	}
   233  	return int(x)
   234  }
   235  
   236  func min(a, b int) int {
   237  	if a < b {
   238  		return a
   239  	}
   240  	return b
   241  }
   242  
   243  func handlerIndex() func(w http.ResponseWriter, r *http.Request) {
   244  	return func(w http.ResponseWriter, r *http.Request) {
   245  		if *verbose {
   246  			log.Printf("reqeust to %s", r.URL)
   247  		}
   248  		if r.URL.Path != "/" {
   249  			w.WriteHeader(http.StatusNotFound)
   250  			return
   251  		}
   252  		if err := index.Execute(w, nil); err != nil {
   253  			w.WriteHeader(http.StatusInternalServerError)
   254  			log.Fatal(err)
   255  			return
   256  		}
   257  	}
   258  }
   259  
   260  var index = template.Must(template.New("").Parse(`
   261  <html>
   262  <body>
   263  <h1>Welcome to WebSocket test server!</h1>
   264  <h4>Ready to Autobahn!</h4>
   265  <a href="/report">Reports</a>
   266  </body>
   267  </html>
   268  `))