Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/build/build/soong/ui/terminal/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 17 kB image not shown  

Quelle  status_test.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package terminal

import (
 "bytes"
 "fmt"
 "os"
 "syscall"
 "testing"

 "android/soong/ui/status"
)

func TestStatusOutput(t *testing.T) {
 tests := []struct {
  name   string
  calls  func(stat status.StatusOutput)
  smart  string
  simple string
 }{
  {
   name:   "two actions",
   calls:  twoActions,
   smart:  "\r\x1b[1m[  00/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 501/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 501/2] action2\x1b[0m\x1b[K\r\x1b[1m[1002/2] action2\x1b[0m\x1b[K\n",
   simple: "[ 501/2] action1\n[1002/2] action2\n",
  },
  {
   name:   "two parallel actions",
   calls:  twoParallelActions,
   smart:  "\r\x1b[1m[  00/2] action1\x1b[0m\x1b[K\r\x1b[1m[  00/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 501/2] action1\x1b[0m\x1b[K\r\x1b[1m[1002/2] action2\x1b[0m\x1b[K\n",
   simple: "[ 501/2] action1\n[1002/2] action2\n",
  },
  {
   name:   "action with output",
   calls:  actionsWithOutput,
   smart:  "\r\x1b[1m[  00/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 331/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 331/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 662/3] action2\x1b[0m\x1b[K\noutput1\noutput2\n\r\x1b[1m[ 662/3] action3\x1b[0m\x1b[K\r\x1b[1m[1003/3] action3\x1b[0m\x1b[K\n",
   simple: "[ 331/3] action1\n[ 662/3] action2\noutput1\noutput2\n[1003/3] action3\n",
  },
  {
   name:   "action with output without newline",
   calls:  actionsWithOutputWithoutNewline,
   smart:  "\r\x1b[1m[  00/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 331/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 331/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 662/3] action2\x1b[0m\x1b[K\noutput1\noutput2\n\r\x1b[1m[ 662/3] action3\x1b[0m\x1b[K\r\x1b[1m[1003/3] action3\x1b[0m\x1b[K\n",
   simple: "[ 331/3] action1\n[ 662/3] action2\noutput1\noutput2\n[1003/3] action3\n",
  },
  {
   name:   "action with error",
   calls:  actionsWithError,
   smart:  "\r\x1b[1m[  00/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 331/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 331/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 662/3] action2\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m f1 f2\ntouch f1 f2\nerror1\nerror2\n\r\x1b[1m[ 662/3] action3\x1b[0m\x1b[K\r\x1b[1m[1003/3] action3\x1b[0m\x1b[K\n",
   simple: "[ 331/3] action1\n[ 662/3] action2\nFAILED: f1 f2\ntouch f1 f2\nerror1\nerror2\n[1003/3] action3\n",
  },
  {
   name:   "action with empty description",
   calls:  actionWithEmptyDescription,
   smart:  "\r\x1b[1m[  00/1] command1\x1b[0m\x1b[K\r\x1b[1m[1001/1] command1\x1b[0m\x1b[K\n",
   simple: "[1001/1] command1\n",
  },
  {
   name:   "messages",
   calls:  actionsWithMessages,
   smart:  "\r\x1b[1m[  00/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 501/2] action1\x1b[0m\x1b[K\r\x1b[1mstatus\x1b[0m\x1b[K\r\x1b[Kprint\n\x1b[31m\x1b[1mFAILED:\x1b[0m error\n\r\x1b[1m[ 501/2] action2\x1b[0m\x1b[K\r\x1b[1m[1002/2] action2\x1b[0m\x1b[K\n",
   simple: "[ 501/2] action1\nstatus\nprint\nFAILED: error\n[1002/2] action2\n",
  },
  {
   name:   "action with long description",
   calls:  actionWithLongDescription,
   smart:  "\r\x1b[1m[  00/2] action with very long descrip\x1b[0m\x1b[K\r\x1b[1m[ 501/2] action with very long descrip\x1b[0m\x1b[K\n",
   simple: "[ 501/2] action with very long description to test eliding\n",
  },
  {
   name:   "action with output with ansi codes",
   calls:  actionWithOutputWithAnsiCodes,
   smart:  "\r\x1b[1m[  00/1] action1\x1b[0m\x1b[K\r\x1b[1m[1001/1] action1\x1b[0m\x1b[K\n\x1b[31mcolor\x1b[0m\n\x1b[31mcolor message\x1b[0m\n",
   simple: "[1001/1] action1\ncolor\ncolor message\n",
  },
 }

 os.Setenv(tableHeightEnVar, "")

 for _, tt := range tests {
  t.Run(tt.name, func(t *testing.T) {

   t.Run("smart", func(t *testing.T) {
    smart := &fakeSmartTerminal{termWidth: 40}
    stat := NewStatusOutput(smart, "", false, false, false, false, false)
    tt.calls(stat)
    stat.Flush()

    if g, w := smart.String(), tt.smart; g != w {
     t.Errorf("want:\n%q\ngot:\n%q", w, g)
    }
   })

   t.Run("simple", func(t *testing.T) {
    simple := &bytes.Buffer{}
    stat := NewStatusOutput(simple, "", false, false, false, false, false)
    tt.calls(stat)
    stat.Flush()

    if g, w := simple.String(), tt.simple; g != w {
     t.Errorf("want:\n%q\ngot:\n%q", w, g)
    }
   })

   t.Run("force simple", func(t *testing.T) {
    smart := &fakeSmartTerminal{termWidth: 40}
    stat := NewStatusOutput(smart, "", true, false, false, false, false)
    tt.calls(stat)
    stat.Flush()

    if g, w := smart.String(), tt.simple; g != w {
     t.Errorf("want:\n%q\ngot:\n%q", w, g)
    }
   })
  })
 }
}

type runner struct {
 counts status.Counts
 stat   status.StatusOutput
}

func newRunner(stat status.StatusOutput, totalActions int) *runner {
 return &runner{
  counts: status.Counts{TotalActions: totalActions},
  stat:   stat,
 }
}

func (r *runner) startAction(action *status.Action) {
 r.counts.StartedActions++
 r.counts.RunningActions++
 r.stat.StartAction(action, r.counts)
}

func (r *runner) finishAction(result status.ActionResult) {
 r.counts.FinishedActions++
 r.counts.RunningActions--
 r.stat.FinishAction(result, r.counts)
}

func (r *runner) finishAndStartAction(result status.ActionResult, action *status.Action) {
 r.counts.FinishedActions++
 r.stat.FinishAction(result, r.counts)

 r.counts.StartedActions++
 r.stat.StartAction(action, r.counts)
}

var (
 action1 = &status.Action{Description: "action1"}
 result1 = status.ActionResult{Action: action1}
 action2 = &status.Action{Description: "action2"}
 result2 = status.ActionResult{Action: action2}
 action3 = &status.Action{Description: "action3"}
 result3 = status.ActionResult{Action: action3}
)

func twoActions(stat status.StatusOutput) {
 runner := newRunner(stat, 2)
 runner.startAction(action1)
 runner.finishAction(result1)
 runner.startAction(action2)
 runner.finishAction(result2)
}

func twoParallelActions(stat status.StatusOutput) {
 runner := newRunner(stat, 2)
 runner.startAction(action1)
 runner.startAction(action2)
 runner.finishAction(result1)
 runner.finishAction(result2)
}

func actionsWithOutput(stat status.StatusOutput) {
 result2WithOutput := status.ActionResult{Action: action2, Output: "output1\noutput2\n"}

 runner := newRunner(stat, 3)
 runner.startAction(action1)
 runner.finishAction(result1)
 runner.startAction(action2)
 runner.finishAction(result2WithOutput)
 runner.startAction(action3)
 runner.finishAction(result3)
}

func actionsWithOutputWithoutNewline(stat status.StatusOutput) {
 result2WithOutputWithoutNewline := status.ActionResult{Action: action2, Output: "output1\noutput2"}

 runner := newRunner(stat, 3)
 runner.startAction(action1)
 runner.finishAction(result1)
 runner.startAction(action2)
 runner.finishAction(result2WithOutputWithoutNewline)
 runner.startAction(action3)
 runner.finishAction(result3)
}

func actionsWithError(stat status.StatusOutput) {
 action2WithError := &status.Action{Description: "action2", Outputs: []string{"f1", "f2"}, Command: "touch f1 f2"}
 result2WithError := status.ActionResult{Action: action2WithError, Output: "error1\nerror2\n", Error: fmt.Errorf("error1")}

 runner := newRunner(stat, 3)
 runner.startAction(action1)
 runner.finishAction(result1)
 runner.startAction(action2WithError)
 runner.finishAction(result2WithError)
 runner.startAction(action3)
 runner.finishAction(result3)
}

func actionWithEmptyDescription(stat status.StatusOutput) {
 action1 := &status.Action{Command: "command1"}
 result1 := status.ActionResult{Action: action1}

 runner := newRunner(stat, 1)
 runner.startAction(action1)
 runner.finishAction(result1)
}

func actionsWithMessages(stat status.StatusOutput) {
 runner := newRunner(stat, 2)

 runner.startAction(action1)
 runner.finishAction(result1)

 stat.Message(status.VerboseLvl, "verbose")
 stat.Message(status.StatusLvl, "status")
 stat.Message(status.PrintLvl, "print")
 stat.Message(status.ErrorLvl, "error")

 runner.startAction(action2)
 runner.finishAction(result2)
}

func actionWithLongDescription(stat status.StatusOutput) {
 action1 := &status.Action{Description: "action with very long description to test eliding"}
 result1 := status.ActionResult{Action: action1}

 runner := newRunner(stat, 2)

 runner.startAction(action1)

 runner.finishAction(result1)
}

func actionWithOutputWithAnsiCodes(stat status.StatusOutput) {
 result1WithOutputWithAnsiCodes := status.ActionResult{Action: action1, Output: "\x1b[31mcolor\x1b[0m"}

 runner := newRunner(stat, 1)
 runner.startAction(action1)
 runner.finishAction(result1WithOutputWithAnsiCodes)

 stat.Message(status.PrintLvl, "\x1b[31mcolor message\x1b[0m")
}

func TestSuppressOutput(t *testing.T) {
 simple := &bytes.Buffer{}
 formatter := newFormatter(false, "", false)
 stat := NewSimpleStatusOutput(simple, formatter, false, false, true)

 runner := newRunner(stat, 2)

 result1WithOutput := status.ActionResult{
  Action: action1,
  Output: "Output1",
 }
 result2WithError := status.ActionResult{
  Action: action2,
  Output: "Output2",
  Error:  fmt.Errorf("Error2"),
 }

 runner.startAction(action1)
 runner.finishAction(result1WithOutput)
 runner.startAction(action2)
 runner.finishAction(result2WithError)

 w := "[ 501/2] action1\n[1002/2] action2\nFAILED: \nOutput2\n"
 if g := simple.String(); g != w {
  t.Errorf("want:\n%q\ngot:\n%q", w, g)
 }
}

func TestSkipActionProgress(t *testing.T) {
 simple := &bytes.Buffer{}
 formatter := newFormatter(false, "", false)
 stat := NewSimpleStatusOutput(simple, formatter, false, true, false)

 runner := newRunner(stat, 2)
 runner.startAction(action1)
 runner.finishAction(result1)
 runner.startAction(action2)
 runner.finishAction(status.ActionResult{Action: action2, Output: "output2"})
 stat.Flush()

 // action1 finished without output -> skipped
 // action2 finished with output -> printed progress + output
 w := "action2\noutput2\n"
 if g := simple.String(); g != w {
  t.Errorf("want:\n%q\ngot:\n%q", w, g)
 }
}

func TestSuppressOutputAndSkipActionProgress(t *testing.T) {
 simple := &bytes.Buffer{}
 formatter := newFormatter(false, "", false)
 stat := NewSimpleStatusOutput(simple, formatter, false, true, true)

 runner := newRunner(stat, 3)

 result3WithError := status.ActionResult{
  Action: action3,
  Output: "Output3",
  Error:  fmt.Errorf("Error3"),
 }

 runner.startAction(action1)
 runner.finishAction(result1)
 runner.startAction(action2)
 runner.finishAction(status.ActionResult{Action: action2, Output: "output2"})
 runner.startAction(action3)
 runner.finishAction(result3WithError)
 stat.Flush()

 // action1 finished without output -> skipped
 // action2 finished with output -> printed progress + output
 w := "action3\nFAILED: \nOutput3\n"
 if g := simple.String(); g != w {
  t.Errorf("want:\n%q\ngot:\n%q", w, g)
 }
}

func TestSmartStatusOutputWidthChange(t *testing.T) {
 os.Setenv(tableHeightEnVar, "")

 smart := &fakeSmartTerminal{termWidth: 40}
 stat := NewStatusOutput(smart, "", false, false, false, false, false)
 smartStat := stat.(*smartStatusOutput)
 smartStat.sigwinchHandled = make(chan bool)

 runner := newRunner(stat, 2)

 action := &status.Action{Description: "action with very long description to test eliding"}
 result := status.ActionResult{Action: action}

 runner.startAction(action)
 smart.termWidth = 30
 // Fake a SIGWINCH
 smartStat.sigwinch <- syscall.SIGWINCH
 <-smartStat.sigwinchHandled
 runner.finishAction(result)

 stat.Flush()

 w := "\r\x1b[1m[  00/2] action with very long descrip\x1b[0m\x1b[K\r\x1b[1m[ 501/2] action with very lo\x1b[0m\x1b[K\n"

 if g := smart.String(); g != w {
  t.Errorf("want:\n%q\ngot:\n%q", w, g)
 }
}

func TestSmartStatusDoesntHideAfterSucecss(t *testing.T) {
 os.Setenv(tableHeightEnVar, "")

 smart := &fakeSmartTerminal{termWidth: 40}
 stat := NewStatusOutput(smart, "", false, false, false, false, false)
 smartStat := stat.(*smartStatusOutput)
 smartStat.sigwinchHandled = make(chan bool)

 runner := newRunner(stat, 2)

 action1 := &status.Action{Description: "action1"}
 result1 := status.ActionResult{
  Action: action1,
  Output: "Output1",
 }

 action2 := &status.Action{Description: "action2"}
 result2 := status.ActionResult{
  Action: action2,
  Output: "Output2",
 }

 runner.startAction(action1)
 runner.startAction(action2)
 runner.finishAction(result1)
 runner.finishAction(result2)

 stat.Flush()

 w := "\r\x1b[1m[  00/2] action1\x1b[0m\x1b[K\r\x1b[1m[  00/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 501/2] action1\x1b[0m\x1b[K\nOutput1\n\r\x1b[1m[1002/2] action2\x1b[0m\x1b[K\nOutput2\n"

 if g := smart.String(); g != w {
  t.Errorf("want:\n%q\ngot:\n%q", w, g)
 }
}

func TestSmartStatusHideAfterFailure(t *testing.T) {
 os.Setenv(tableHeightEnVar, "")

 smart := &fakeSmartTerminal{termWidth: 40}
 stat := NewStatusOutput(smart, "", false, false, false, false, false)
 smartStat := stat.(*smartStatusOutput)
 smartStat.sigwinchHandled = make(chan bool)

 runner := newRunner(stat, 2)

 action1 := &status.Action{Description: "action1"}
 result1 := status.ActionResult{
  Action: action1,
  Output: "Output1",
  Error:  fmt.Errorf("Error1"),
 }

 action2 := &status.Action{Description: "action2"}
 result2 := status.ActionResult{
  Action: action2,
  Output: "Output2",
 }

 runner.startAction(action1)
 runner.startAction(action2)
 runner.finishAction(result1)
 runner.finishAction(result2)

 stat.Flush()

 w := "\r\x1b[1m[  00/2] action1\x1b[0m\x1b[K\r\x1b[1m[  00/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 501/2] action1\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput1\n\r\x1b[1m[1002/2] action2\x1b[0m\x1b[K\nThere was 1 action that completed after the action that failed. See verbose.log.gz for its output.\n"

 if g := smart.String(); g != w {
  t.Errorf("want:\n%q\ngot:\n%q", w, g)
 }
}

func TestSmartStatusHideAfterFailurePlural(t *testing.T) {
 os.Setenv(tableHeightEnVar, "")

 smart := &fakeSmartTerminal{termWidth: 40}
 stat := NewStatusOutput(smart, "", false, false, false, false, false)
 smartStat := stat.(*smartStatusOutput)
 smartStat.sigwinchHandled = make(chan bool)

 runner := newRunner(stat, 2)

 action1 := &status.Action{Description: "action1"}
 result1 := status.ActionResult{
  Action: action1,
  Output: "Output1",
  Error:  fmt.Errorf("Error1"),
 }

 action2 := &status.Action{Description: "action2"}
 result2 := status.ActionResult{
  Action: action2,
  Output: "Output2",
 }

 action3 := &status.Action{Description: "action3"}
 result3 := status.ActionResult{
  Action: action3,
  Output: "Output3",
 }

 runner.startAction(action1)
 runner.startAction(action2)
 runner.startAction(action3)
 runner.finishAction(result1)
 runner.finishAction(result2)
 runner.finishAction(result3)

 stat.Flush()

 w := "\r\x1b[1m[  00/2] action1\x1b[0m\x1b[K\r\x1b[1m[  00/2] action2\x1b[0m\x1b[K\r\x1b[1m[  00/2] action3\x1b[0m\x1b[K\r\x1b[1m[ 501/2] action1\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput1\n\r\x1b[1m[1002/2] action2\x1b[0m\x1b[K\r\x1b[1m[1503/2] action3\x1b[0m\x1b[K\nThere were 2 actions that completed after the action that failed. See verbose.log.gz for their output.\n"

 if g := smart.String(); g != w {
  t.Errorf("want:\n%q\ngot:\n%q", w, g)
 }
}

func TestSmartStatusDontHideErrorAfterFailure(t *testing.T) {
 os.Setenv(tableHeightEnVar, "")

 smart := &fakeSmartTerminal{termWidth: 40}
 stat := NewStatusOutput(smart, "", false, false, false, false, false)
 smartStat := stat.(*smartStatusOutput)
 smartStat.sigwinchHandled = make(chan bool)

 runner := newRunner(stat, 2)

 action1 := &status.Action{Description: "action1"}
 result1 := status.ActionResult{
  Action: action1,
  Output: "Output1",
  Error:  fmt.Errorf("Error1"),
 }

 action2 := &status.Action{Description: "action2"}
 result2 := status.ActionResult{
  Action: action2,
  Output: "Output2",
  Error:  fmt.Errorf("Error1"),
 }

 runner.startAction(action1)
 runner.startAction(action2)
 runner.finishAction(result1)
 runner.finishAction(result2)

 stat.Flush()

 w := "\r\x1b[1m[  00/2] action1\x1b[0m\x1b[K\r\x1b[1m[  00/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 501/2] action1\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput1\n\r\x1b[1m[1002/2] action2\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput2\n"

 if g := smart.String(); g != w {
  t.Errorf("want:\n%q\ngot:\n%q", w, g)
 }
}

func TestSmartStatusSuppressOutput(t *testing.T) {
 os.Setenv(tableHeightEnVar, "")

 smart := &fakeSmartTerminal{termWidth: 40}
 stat := NewStatusOutput(smart, "", false, false, false, false, true)
 smartStat := stat.(*smartStatusOutput)
 smartStat.sigwinchHandled = make(chan bool)

 runner := newRunner(stat, 2)

 action1 := &status.Action{Description: "action1"}
 result1 := status.ActionResult{
  Action: action1,
  Output: "Output1",
 }

 action2 := &status.Action{Description: "action2"}
 result2 := status.ActionResult{
  Action: action2,
  Output: "Output2",
  Error:  fmt.Errorf("Error1"),
 }

 runner.startAction(action1)
 runner.startAction(action2)
 runner.finishAction(result1)
 runner.finishAction(result2)

 stat.Flush()

 w := "\r\x1b[1m[  00/2] action1\x1b[0m\x1b[K\r\x1b[1m[  00/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 501/2] action1\x1b[0m\x1b[K\r\x1b[1m[1002/2] action2\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput2\nThere was 1 action that completed after the action that failed. See verbose.log.gz for its output.\n"

 if g := smart.String(); g != w {
  t.Errorf("want:\n%q\ngot:\n%q", w, g)
 }
}

[Dauer der Verarbeitung: 0.37 Sekunden, vorverarbeitet 2026-06-28]