Refactor code for improved readability and maintainability; add utility functions for score calculation and archive processing
This commit is contained in:
@@ -11,14 +11,13 @@ import (
|
||||
"github.com/0x524a/onvif-go/discovery"
|
||||
)
|
||||
|
||||
const defaultDiscoveryTimeout = 10 * time.Second
|
||||
|
||||
func main() {
|
||||
iface := flag.String("interface", "", "Network interface to use (e.g., en0, en11)")
|
||||
timeout := flag.Duration("timeout", 10*time.Second, "Discovery timeout")
|
||||
timeout := flag.Duration("timeout", defaultDiscoveryTimeout, "Discovery timeout")
|
||||
flag.Parse()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), *timeout)
|
||||
defer cancel()
|
||||
|
||||
opts := &discovery.DiscoverOptions{
|
||||
NetworkInterface: *iface,
|
||||
}
|
||||
@@ -29,9 +28,13 @@ func main() {
|
||||
}
|
||||
fmt.Println("...")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), *timeout)
|
||||
defer cancel()
|
||||
|
||||
devices, err := discovery.DiscoverWithOptions(ctx, *timeout, opts)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Discovery error: %v\n", err)
|
||||
cancel()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
|
||||
+26
-15
@@ -14,6 +14,11 @@ import (
|
||||
onviftesting "github.com/0x524a/onvif-go/testing"
|
||||
)
|
||||
|
||||
const (
|
||||
maxTokenLength = 20
|
||||
percentScale = 100
|
||||
)
|
||||
|
||||
var (
|
||||
captureArchive = flag.String("capture", "", "Path to XML capture archive (.tar.gz)")
|
||||
outputDir = flag.String("output", "./", "Output directory for generated test file")
|
||||
@@ -128,7 +133,7 @@ type GeneratedTest struct {
|
||||
Code string
|
||||
}
|
||||
|
||||
// operationInfo holds info about captured operations
|
||||
// operationInfo holds info about captured operations.
|
||||
type operationInfo struct {
|
||||
OperationName string
|
||||
ServiceType onviftesting.ServiceType
|
||||
@@ -199,7 +204,8 @@ func generateTests() string {
|
||||
metadata.CameraInfo.FirmwareVersion)
|
||||
} else {
|
||||
// Try to extract from GetDeviceInformation response
|
||||
for _, ex := range capture.Exchanges {
|
||||
for i := range capture.Exchanges {
|
||||
ex := &capture.Exchanges[i]
|
||||
if ex.OperationName == "GetDeviceInformation" && ex.Success {
|
||||
manufacturer := extractXMLValue(ex.ResponseBody, "Manufacturer")
|
||||
model := extractXMLValue(ex.ResponseBody, "Model")
|
||||
@@ -207,6 +213,7 @@ func generateTests() string {
|
||||
if manufacturer != "" && model != "" {
|
||||
cameraDesc = fmt.Sprintf("%s %s (Firmware: %s)", manufacturer, model, firmware)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -241,12 +248,9 @@ func generateTests() string {
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create output file: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = f.Close()
|
||||
}()
|
||||
defer f.Close()
|
||||
|
||||
if err := tmpl.Execute(f, testData); err != nil {
|
||||
_ = f.Close()
|
||||
log.Fatalf("Failed to execute template: %v", err)
|
||||
}
|
||||
|
||||
@@ -264,10 +268,11 @@ func generateTests() string {
|
||||
}
|
||||
|
||||
func analyzeOperations(capture *onviftesting.CameraCaptureV2) []operationInfo {
|
||||
var ops []operationInfo
|
||||
ops := make([]operationInfo, 0, len(capture.Exchanges))
|
||||
seen := make(map[string]bool)
|
||||
|
||||
for _, ex := range capture.Exchanges {
|
||||
for i := range capture.Exchanges {
|
||||
ex := &capture.Exchanges[i]
|
||||
// Create unique key for deduplication
|
||||
key := ex.OperationName
|
||||
if token := ex.GetProfileToken(); token != "" {
|
||||
@@ -297,10 +302,13 @@ func analyzeOperations(capture *onviftesting.CameraCaptureV2) []operationInfo {
|
||||
func hasNonDeviceOperations(ops []operationInfo) bool {
|
||||
for _, op := range ops {
|
||||
switch op.ServiceType {
|
||||
case onviftesting.ServiceMedia, onviftesting.ServicePTZ, onviftesting.ServiceImaging:
|
||||
case onviftesting.ServiceMedia, onviftesting.ServicePTZ, onviftesting.ServiceImaging, onviftesting.ServiceEvent, onviftesting.ServiceDeviceIO:
|
||||
return true
|
||||
case onviftesting.ServiceDevice, onviftesting.ServiceUnknown:
|
||||
// continue checking
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -574,6 +582,7 @@ func generatePTZTests(ops []operationInfo) []GeneratedTest {
|
||||
Code: code,
|
||||
})
|
||||
delete(ptzOps, op.OperationName)
|
||||
|
||||
continue
|
||||
}
|
||||
if token, ok := op.Parameters["ProfileToken"].(string); ok && token != "" {
|
||||
@@ -699,9 +708,10 @@ func sanitizeToken(token string) string {
|
||||
token = strings.ReplaceAll(token, ".", "_")
|
||||
token = strings.ReplaceAll(token, " ", "_")
|
||||
// Truncate if too long
|
||||
if len(token) > 20 {
|
||||
token = token[:20]
|
||||
if len(token) > maxTokenLength {
|
||||
token = token[:maxTokenLength]
|
||||
}
|
||||
|
||||
return token
|
||||
}
|
||||
|
||||
@@ -774,7 +784,7 @@ func updateCameraRegistry(regPath, archivePath, testFile string) {
|
||||
}
|
||||
|
||||
// Add or update the camera entry
|
||||
registry.AddCamera(*entry)
|
||||
registry.AddCamera(entry)
|
||||
|
||||
// Update coverage statistics
|
||||
updateRegistryCoverage(registry, archivePath)
|
||||
@@ -874,7 +884,7 @@ func generateCoverageMarkdown(registry *onviftesting.Registry) string {
|
||||
total, captured := registry.GetTotalCoverage()
|
||||
if total > 0 {
|
||||
sb.WriteString(fmt.Sprintf("- **Overall Coverage**: %.1f%% (%d/%d operations)\n\n",
|
||||
float64(captured)/float64(total)*100, captured, total))
|
||||
float64(captured)/float64(total)*percentScale, captured, total))
|
||||
}
|
||||
|
||||
// Cameras
|
||||
@@ -883,7 +893,8 @@ func generateCoverageMarkdown(registry *onviftesting.Registry) string {
|
||||
sb.WriteString("| Manufacturer | Model | Firmware | Operations | Capabilities |\n")
|
||||
sb.WriteString("|--------------|-------|----------|------------|---------------|\n")
|
||||
|
||||
for _, cam := range registry.Cameras {
|
||||
for i := range registry.Cameras {
|
||||
cam := ®istry.Cameras[i]
|
||||
caps := strings.Join(cam.Capabilities, ", ")
|
||||
sb.WriteString(fmt.Sprintf("| %s | %s | %s | %d | %s |\n",
|
||||
cam.Manufacturer, cam.Model, cam.Firmware, cam.OperationsCaptured, caps))
|
||||
@@ -902,7 +913,7 @@ func generateCoverageMarkdown(registry *onviftesting.Registry) string {
|
||||
if cov, ok := registry.Coverage[service]; ok {
|
||||
pct := 0.0
|
||||
if cov.Total > 0 {
|
||||
pct = float64(cov.Captured) / float64(cov.Total) * 100
|
||||
pct = float64(cov.Captured) / float64(cov.Total) * percentScale
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf("| %s | %d | %d | %.1f%% |\n",
|
||||
service, cov.Total, cov.Captured, pct))
|
||||
|
||||
@@ -30,6 +30,7 @@ const (
|
||||
retryDelaySec = 5
|
||||
maxIdleTimeoutSec = 90
|
||||
unknownStatus = "Unknown"
|
||||
percentScale = 100
|
||||
)
|
||||
|
||||
type CameraReport struct {
|
||||
@@ -1332,7 +1333,7 @@ func runComprehensiveCapture(ctx context.Context, client *onvif.Client, report *
|
||||
fmt.Printf(" Total operations: %d\n", totalOps)
|
||||
fmt.Printf(" Successful: %d\n", successCount)
|
||||
fmt.Printf(" Failed: %d\n", failCount)
|
||||
fmt.Printf(" Success rate: %.1f%%\n", float64(successCount)/float64(totalOps)*100)
|
||||
fmt.Printf(" Success rate: %.1f%%\n", float64(successCount)/float64(totalOps)*percentScale)
|
||||
fmt.Println("========================================")
|
||||
}
|
||||
|
||||
@@ -1633,6 +1634,73 @@ func extractSOAPOperation(soapBody string) string {
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
// compareFileOrder determines sort order for tar archive entries.
|
||||
// Returns true if file i should come before file j.
|
||||
func compareFileOrder(i, j int, files []string) bool {
|
||||
nameI := filepath.Base(files[i])
|
||||
nameJ := filepath.Base(files[j])
|
||||
|
||||
// metadata.json always first
|
||||
if nameI == "metadata.json" {
|
||||
return true
|
||||
}
|
||||
if nameJ == "metadata.json" {
|
||||
return false
|
||||
}
|
||||
|
||||
// JSON files before XML files
|
||||
isJSONi := strings.HasSuffix(nameI, ".json")
|
||||
isJSONj := strings.HasSuffix(nameJ, ".json")
|
||||
if isJSONi && !isJSONj {
|
||||
return true
|
||||
}
|
||||
if !isJSONi && isJSONj {
|
||||
return false
|
||||
}
|
||||
|
||||
// Sort by name
|
||||
return nameI < nameJ
|
||||
}
|
||||
|
||||
// writeTarEntry writes a single file to the tar archive.
|
||||
func writeTarEntry(tarWriter *tar.Writer, sourceDir, path string) error {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to stat file: %w", err)
|
||||
}
|
||||
|
||||
// Create tar header
|
||||
header, err := tar.FileInfoHeader(info, "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create tar header: %w", err)
|
||||
}
|
||||
|
||||
// Set name to relative path
|
||||
relPath, err := filepath.Rel(sourceDir, path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get relative path: %w", err)
|
||||
}
|
||||
header.Name = relPath
|
||||
|
||||
// Write header
|
||||
if err := tarWriter.WriteHeader(header); err != nil {
|
||||
return fmt.Errorf("failed to write tar header: %w", err)
|
||||
}
|
||||
|
||||
// Write file content
|
||||
file, err := os.Open(path) //nolint:gosec // File path is from filepath.Walk, safe
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open file: %w", err)
|
||||
}
|
||||
|
||||
if _, err := io.Copy(tarWriter, file); err != nil {
|
||||
_ = file.Close()
|
||||
return fmt.Errorf("failed to write file to tar: %w", err)
|
||||
}
|
||||
_ = file.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// createTarGzV2 creates a V2 tar.gz archive with metadata.json first.
|
||||
func createTarGzV2(sourceDir, archivePath string) error {
|
||||
// Create archive file
|
||||
@@ -1673,67 +1741,14 @@ func createTarGzV2(sourceDir, archivePath string) error {
|
||||
|
||||
// Sort files: metadata.json first, then capture JSON files in order, then XML files
|
||||
sort.Slice(files, func(i, j int) bool {
|
||||
nameI := filepath.Base(files[i])
|
||||
nameJ := filepath.Base(files[j])
|
||||
|
||||
// metadata.json always first
|
||||
if nameI == "metadata.json" {
|
||||
return true
|
||||
}
|
||||
if nameJ == "metadata.json" {
|
||||
return false
|
||||
}
|
||||
|
||||
// JSON files before XML files
|
||||
isJSONi := strings.HasSuffix(nameI, ".json")
|
||||
isJSONj := strings.HasSuffix(nameJ, ".json")
|
||||
if isJSONi && !isJSONj {
|
||||
return true
|
||||
}
|
||||
if !isJSONi && isJSONj {
|
||||
return false
|
||||
}
|
||||
|
||||
// Sort by name
|
||||
return nameI < nameJ
|
||||
return compareFileOrder(i, j, files)
|
||||
})
|
||||
|
||||
// Write files in sorted order
|
||||
for _, path := range files {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to stat file: %w", err)
|
||||
if err := writeTarEntry(tarWriter, sourceDir, path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create tar header
|
||||
header, err := tar.FileInfoHeader(info, "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create tar header: %w", err)
|
||||
}
|
||||
|
||||
// Set name to relative path
|
||||
relPath, err := filepath.Rel(sourceDir, path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get relative path: %w", err)
|
||||
}
|
||||
header.Name = relPath
|
||||
|
||||
// Write header
|
||||
if err := tarWriter.WriteHeader(header); err != nil {
|
||||
return fmt.Errorf("failed to write tar header: %w", err)
|
||||
}
|
||||
|
||||
// Write file content
|
||||
file, err := os.Open(path) //nolint:gosec // File path is from filepath.Walk, safe
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open file: %w", err)
|
||||
}
|
||||
|
||||
if _, err := io.Copy(tarWriter, file); err != nil {
|
||||
_ = file.Close()
|
||||
return fmt.Errorf("failed to write file to tar: %w", err)
|
||||
}
|
||||
_ = file.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user