developer's diary

最近はc#のエントリが多いです

C# (dotnetcore)で標準出力を利用して進行中のスピナーを作る (5)

前回の記事

mitsugeek.net

課題

  • Consoleへの操作部分のテストをどうするか。

一つの解決策

Console.SetOutを利用して、Console.Outの出力先を変えてしまう。

        [Fact]
        public void ConsoleTest()
        {
            var output = new System.IO.StringWriter();
            Console.SetOut(output);
            ConsoleProgressSpiner c = new ConsoleProgressSpiner();
            c.Minimum = 0;
            c.Maximum = 100;
            Assert.Equal("", output.ToString());
            c.Value = 0;
            Assert.Equal("⠋  0%", output.ToString());
        }

MinimumとMaximumの設定時にも、updateを走らせていたが、これを廃止

テストしてみるとエラーになったので、少し修正した。

    /// <summary>コンソール用の</summary>
    public class ConsoleProgressSpiner
    {

        //アニメーションのコマ
        string[] bars = { "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" };


        private int minimum = 0;

        /// <summary>コントロールの範囲の最小値を取得または設定します。</summary>
        public int Minimum
        {
            get
            {
                return minimum;
            }
            set
            {
                if (value < 0)
                    throw new ArgumentException("プロパティに対して指定された値が 0 未満です。");

                minimum = value;

                if (this.minimum > this.value)
                {
                    this.value = this.minimum;
                }
            }
        }

        private int maximum = 100;

        /// <summary>コントロールの範囲の最大値を取得または設定します。</summary>
        public int Maximum
        {
            get
            {
                return maximum;
            }
            set
            {
                if (value < 0)
                    throw new ArgumentException("プロパティに対して指定された値が 0 未満です。");

                maximum = value;

                if (this.maximum < this.value)
                {
                    this.value = this.maximum;
                }
            }
        }

        private int value = 0;

        /// <summary>プログレス バーの現在位置を取得または設定します。</summary>
        public int Value
        {
            get
            {
                return this.value;
            }
            set
            {
                if (value < this.Minimum)
                    throw new ArgumentException("指定された値が Minimum プロパティの値未満です。");

                if (value > this.Maximum)
                    throw new ArgumentException("指定された値が Maximum プロパティの値を超えます。");

                this.value = value;

                update();
            }
        }

        /// <summary>PerformStep() メソッドを呼び出したときに、プログレス バーの現在の位置を進める量を取得または設定します。</summary>
        public int Step { get; set; }

        /// <summary>プログレス バーの現在位置を Step プロパティの値の分だけ進めます。 </summary>
        public void PerformStep()
        {

        }

        /// <summary>Minimum〜Maximumに対するValueの進捗率を計算して返却</summary>
        /// <returns>進捗率(%)</returns>
        private int CalcProportion()
        {
            decimal oneMeter = new decimal(100) / (this.Maximum - this.Minimum);
            return Decimal.ToInt32(oneMeter * (this.Value - this.Minimum));
        }

        /// <summary>進捗終了の印字</summary>
        private void DoneProgressConsoleWrite()
        {
            //色を戻す
            Console.ResetColor();

            //カーソル位置を変更
            Console.SetCursorPosition(0, Console.CursorTop);

            //終了の表示
            Console.WriteLine("Done! ");
        }

        /// <summary>進捗進行中の印字</summary>
        private void InProgressConsoleWrite()
        {
            //進捗率計算
            int proportion = CalcProportion();

            //カーソル位置を変更
            Console.SetCursorPosition(0, Console.CursorTop);

            //色変更
            Console.ForegroundColor = ConsoleColor.Green;
            Console.BackgroundColor = ConsoleColor.Black;

            //進捗率の印字
            Console.Write("{0}{1, 3:d0}%", bars[proportion % bars.Length], proportion);
        }

        /// <summary>描画</summary>
        private void update()
        {
            if (this.Maximum == this.Value)
            {
                DoneProgressConsoleWrite();
            }
            else
            {
                InProgressConsoleWrite();
            }
        }
    }

スピナーが1週回るまでテストを書いてみる

        [Fact]
        public void ConsoleTest()
        {
            var output = new System.IO.StringWriter();
            Console.SetOut(output);
            ConsoleProgressSpiner c = new ConsoleProgressSpiner();
            c.Minimum = 0;
            c.Maximum = 100;
            Assert.Equal("", output.ToString());
            c.Value = 0;
            Assert.Equal("⠋  0%", output.ToString());
            c.Value = 1;
            Assert.Equal("⠋  0%⠙  1%", output.ToString());
            c.Value = 2;
            Assert.Equal("⠋  0%⠙  1%⠹  2%", output.ToString());
            c.Value = 3;
            Assert.Equal("⠋  0%⠙  1%⠹  2%⠸  3%", output.ToString());
            c.Value = 4;
            Assert.Equal("⠋  0%⠙  1%⠹  2%⠸  3%⠼  4%", output.ToString());
            c.Value = 5;
            Assert.Equal("⠋  0%⠙  1%⠹  2%⠸  3%⠼  4%⠴  5%", output.ToString());
            c.Value = 6;
            Assert.Equal("⠋  0%⠙  1%⠹  2%⠸  3%⠼  4%⠴  5%⠦  6%", output.ToString());
            c.Value = 7;
            Assert.Equal("⠋  0%⠙  1%⠹  2%⠸  3%⠼  4%⠴  5%⠦  6%⠧  7%", output.ToString());
            c.Value = 8;
            Assert.Equal("⠋  0%⠙  1%⠹  2%⠸  3%⠼  4%⠴  5%⠦  6%⠧  7%⠇  8%", output.ToString());
            c.Value = 9;
            Assert.Equal("⠋  0%⠙  1%⠹  2%⠸  3%⠼  4%⠴  5%⠦  6%⠧  7%⠇  8%⠏  9%", output.ToString());
            c.Value = 10;
            Assert.Equal("⠋  0%⠙  1%⠹  2%⠸  3%⠼  4%⠴  5%⠦  6%⠧  7%⠇  8%⠏  9%⠋ 10%", output.ToString());

        }

少し不細工。

だけど、毎回 var output = new System.IO.StringWriter();するのは少し違う気もする。

MinimumとMaximumを変更してのテスト

0〜500までの数字で、100%までの進捗率を求める部分の試験


        [Fact]
        public void ConsoleCalcTest()
        {
            var output = new System.IO.StringWriter();
            Console.SetOut(output);
            ConsoleProgressSpiner c = new ConsoleProgressSpiner();
            c.Minimum = 0;
            c.Maximum = 500;
            Assert.Equal("", output.ToString());
            c.Value = 0;
            Assert.Equal("⠋  0%", output.ToString());
            c.Value = 1;
            Assert.Equal("⠋  0%⠋  0%", output.ToString());
            c.Value = 2;
            Assert.Equal("⠋  0%⠋  0%⠋  0%", output.ToString());
            c.Value = 3;
            Assert.Equal("⠋  0%⠋  0%⠋  0%⠋  0%", output.ToString());
            c.Value = 4;
            Assert.Equal("⠋  0%⠋  0%⠋  0%⠋  0%⠋  0%", output.ToString());
            c.Value = 5;
            Assert.Equal("⠋  0%⠋  0%⠋  0%⠋  0%⠋  0%⠙  1%", output.ToString());
            c.Value = 6;
            Assert.Equal("⠋  0%⠋  0%⠋  0%⠋  0%⠋  0%⠙  1%⠙  1%", output.ToString());
            c.Value = 7;
            Assert.Equal("⠋  0%⠋  0%⠋  0%⠋  0%⠋  0%⠙  1%⠙  1%⠙  1%", output.ToString());
            c.Value = 8;
            Assert.Equal("⠋  0%⠋  0%⠋  0%⠋  0%⠋  0%⠙  1%⠙  1%⠙  1%⠙  1%", output.ToString());
            c.Value = 9;
            Assert.Equal("⠋  0%⠋  0%⠋  0%⠋  0%⠋  0%⠙  1%⠙  1%⠙  1%⠙  1%⠙  1%", output.ToString());
            c.Value = 10;
            Assert.Equal("⠋  0%⠋  0%⠋  0%⠋  0%⠋  0%⠙  1%⠙  1%⠙  1%⠙  1%⠙  1%⠹  2%", output.ToString());

        }

ひとまず。ここまで。続きは次回。