# frozen_string_literal: true

RSpec.describe 'RuboCop::CLI --auto-gen-config', :isolated_environment do # rubocop:disable RSpec/DescribeClass
  subject(:cli) { RuboCop::CLI.new }

  include_context 'cli spec behavior'

  describe '--auto-gen-config' do
    before do
      RuboCop::Formatter::DisabledConfigFormatter.config_to_allow_offenses = {}
      RuboCop::Formatter::DisabledConfigFormatter.detected_styles = {}
    end

    shared_examples 'LineLength handling' do |ctx, initial_dotfile, exp_dotfile|
      context ctx do
        # Since there is a line with length 99 in the inspected code,
        # Style/IfUnlessModifier will register an offense when
        # Layout/LineLength:Max has been set to 99. With a lower
        # LineLength:Max there would be no IfUnlessModifier offense.
        it "bases other cops' configuration on the code base's current maximum line length" do
          if initial_dotfile
            initial_config = YAML.safe_load(initial_dotfile.join($RS)) || {}
            inherited_files = Array(initial_config['inherit_from'])
            (inherited_files - ['.rubocop.yml']).each { |f| create_empty_file(f) }

            create_file('.rubocop.yml', initial_dotfile)
            create_file('.rubocop_todo.yml', [''])
          end
          create_file('example.rb', <<~RUBY)
            # frozen_string_literal: true

            def f
            #{'  #' * 46}
              if #{'a' * 120}
                return y
              end

              z
            end
          RUBY
          expect(cli.run(['--auto-gen-config'])).to eq(0)
          expect(File.readlines('.rubocop_todo.yml')
                   .drop_while { |line| line.start_with?('#') }.join)
            .to eq(<<~YAML)

              # Offense count: 1
              # This cop supports safe autocorrection (--autocorrect).
              Style/IfUnlessModifier:
                Exclude:
                  - 'example.rb'

              # Offense count: 2
              # This cop supports safe autocorrection (--autocorrect).
              # Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
              # URISchemes: http, https
              Layout/LineLength:
                Max: 138
          YAML
          expect(File.read('.rubocop.yml').strip).to eq(exp_dotfile.join($RS))
          $stdout = StringIO.new
          expect(RuboCop::CLI.new.run([])).to eq(0)
          expect($stderr.string).to eq('')
          expect($stdout.string).to include('no offenses detected')
        end
      end
    end

    it_behaves_like 'LineLength handling',
                    'when .rubocop.yml does not exist',
                    nil,
                    ['inherit_from: .rubocop_todo.yml']

    it_behaves_like 'LineLength handling',
                    'when .rubocop.yml is empty',
                    [''],
                    ['inherit_from: .rubocop_todo.yml']

    it_behaves_like 'LineLength handling',
                    'when .rubocop.yml inherits only from .rubocop_todo.yml',
                    ['inherit_from: .rubocop_todo.yml'],
                    ['inherit_from: .rubocop_todo.yml']

    it_behaves_like 'LineLength handling',
                    'when .rubocop.yml inherits only from .rubocop_todo.yml ' \
                    'in an array',
                    ['inherit_from:',
                     '  - .rubocop_todo.yml'],
                    ['inherit_from:',
                     '  - .rubocop_todo.yml']

    it_behaves_like 'LineLength handling',
                    'when .rubocop.yml inherits from another file and ' \
                    '.rubocop_todo.yml',
                    ['inherit_from:',
                     '  - common.yml',
                     '  - .rubocop_todo.yml'],
                    ['inherit_from:',
                     '  - common.yml',
                     '  - .rubocop_todo.yml']

    it_behaves_like 'LineLength handling',
                    'when .rubocop.yml inherits from two other files',
                    ['inherit_from:',
                     '  - common1.yml',
                     '  - common2.yml'],
                    ['inherit_from:',
                     '  - .rubocop_todo.yml',
                     '  - common1.yml',
                     '  - common2.yml']

    it_behaves_like 'LineLength handling',
                    'when .rubocop.yml inherits from another file',
                    ['inherit_from: common.yml'],
                    ['inherit_from:',
                     '  - .rubocop_todo.yml',
                     '  - common.yml']

    it_behaves_like 'LineLength handling',
                    "when .rubocop.yml doesn't inherit",
                    ['Style/For:',
                     '  Enabled: false'],
                    ['inherit_from: .rubocop_todo.yml',
                     '',
                     'Style/For:',
                     '  Enabled: false']

    context 'with Layout/LineLength:Max overridden' do
      before do
        create_file('.rubocop.yml', ['Layout/LineLength:',
                                     "  Max: #{line_length_max}",
                                     "  Enabled: #{line_length_enabled}"])
        create_file('.rubocop_todo.yml', [''])
        create_file('example.rb', <<~RUBY)
          def f
          #{'  #' * 33}
            if #{'a' * 80}
              return y
            end

            z
          end
        RUBY
      end

      context 'when .rubocop.yml has Layout/LineLength:Max less than code base max' do
        let(:line_length_max) { 90 }
        let(:line_length_enabled) { true }

        it "bases other cops' configuration on the overridden LineLength:Max" do
          expect(cli.run(['--auto-gen-config'])).to eq(0)
          expect($stdout.string).to include(<<~YAML)
            Added inheritance from `.rubocop_todo.yml` in `.rubocop.yml`.
            Phase 1 of 2: run Layout/LineLength cop (skipped because the default Layout/LineLength:Max is overridden)
            Phase 2 of 2: run all cops
          YAML
          # Layout/LineLength gets an Exclude property because Max is set in .rubocop.yml.
          #
          # Note that there is no Style/IfUnlessModifier offense registered due to the Max:90
          # setting.
          expect(File.readlines('.rubocop_todo.yml')
                  .drop_while { |line| line.start_with?('#') }.join)
            .to eq(<<~YAML)

              # Offense count: 1
              # This cop supports safe autocorrection (--autocorrect).
              # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
              # URISchemes: http, https
              Layout/LineLength:
                Exclude:
                  - 'example.rb'

              # Offense count: 1
              # This cop supports unsafe autocorrection (--autocorrect-all).
              # Configuration parameters: EnforcedStyle.
              # SupportedStyles: always, always_true, never
              Style/FrozenStringLiteralComment:
                Exclude:
                  - '**/*.arb'
                  - 'example.rb'
            YAML
          expect(File.read('.rubocop.yml')).to eq(<<~YAML)
            inherit_from: .rubocop_todo.yml

            Layout/LineLength:
              Max: 90
              Enabled: true
          YAML
          expect(RuboCop::CLI.new.run([])).to eq(0)
        end
      end

      context 'when .rubocop.yml has Layout/LineLength disabled' do
        let(:line_length_max) { 90 }
        let(:line_length_enabled) { false }

        it 'skips the cop from both phases of the run' do
          expect(cli.run(['--auto-gen-config'])).to eq(0)
          expect($stdout.string).to include(<<~YAML)
            Added inheritance from `.rubocop_todo.yml` in `.rubocop.yml`.
            Phase 1 of 2: run Layout/LineLength cop (skipped because Layout/LineLength is disabled)
            Phase 2 of 2: run all cops
          YAML

          # The code base max line length is 99, but the setting Enabled: false
          # overrides that so no Layout/LineLength:Max setting is generated in
          # .rubocop_todo.yml.
          expect(File.readlines('.rubocop_todo.yml')
                  .drop_while { |line| line.start_with?('#') }.join)
            .to eq(<<~YAML)

              # Offense count: 1
              # This cop supports unsafe autocorrection (--autocorrect-all).
              # Configuration parameters: EnforcedStyle.
              # SupportedStyles: always, always_true, never
              Style/FrozenStringLiteralComment:
                Exclude:
                  - '**/*.arb'
                  - 'example.rb'

              # Offense count: 1
              # This cop supports safe autocorrection (--autocorrect).
              Style/IfUnlessModifier:
                Exclude:
                  - 'example.rb'
            YAML
          expect(File.read('.rubocop.yml')).to eq(<<~YAML)
            inherit_from: .rubocop_todo.yml

            Layout/LineLength:
              Max: 90
              Enabled: false
          YAML
          $stdout = StringIO.new
          expect(RuboCop::CLI.new.run(%w[--format simple])).to eq(0)
          expect($stderr.string).to eq('')
          expect($stdout.string).to eq(<<~OUTPUT)

            1 file inspected, no offenses detected
          OUTPUT
        end
      end

      context 'when .rubocop.yml has Layout/LineLength:Max more than code base max' do
        let(:line_length_max) { 150 }
        let(:line_length_enabled) { true }

        it "bases other cops' configuration on the overridden LineLength:Max" do
          expect(cli.run(['--auto-gen-config'])).to eq(0)
          expect($stdout.string).to include(<<~YAML)
            Added inheritance from `.rubocop_todo.yml` in `.rubocop.yml`.
            Phase 1 of 2: run Layout/LineLength cop (skipped because the default Layout/LineLength:Max is overridden)
            Phase 2 of 2: run all cops
          YAML
          # The code base max line length is 99, but the setting Max:150
          # overrides that so no Layout/LineLength:Max setting is generated in
          # .rubocop_todo.yml.
          expect(File.readlines('.rubocop_todo.yml')
                  .drop_while { |line| line.start_with?('#') }.join)
            .to eq(<<~YAML)

              # Offense count: 1
              # This cop supports unsafe autocorrection (--autocorrect-all).
              # Configuration parameters: EnforcedStyle.
              # SupportedStyles: always, always_true, never
              Style/FrozenStringLiteralComment:
                Exclude:
                  - '**/*.arb'
                  - 'example.rb'

              # Offense count: 1
              # This cop supports safe autocorrection (--autocorrect).
              Style/IfUnlessModifier:
                Exclude:
                  - 'example.rb'
            YAML
          expect(File.read('.rubocop.yml')).to eq(<<~YAML)
            inherit_from: .rubocop_todo.yml

            Layout/LineLength:
              Max: 150
              Enabled: true
          YAML
          $stdout = StringIO.new
          expect(RuboCop::CLI.new.run(%w[--format simple])).to eq(0)
          expect($stderr.string).to eq('')
          expect($stdout.string).to eq(<<~OUTPUT)

            1 file inspected, no offenses detected
          OUTPUT
        end
      end
    end

    shared_examples 'overwrites todo file' do |description, code|
      context "when .rubocop.yml contains #{description}" do
        it 'overwrites an existing todo file' do
          create_file('example1.rb', ['# frozen_string_literal: true',
                                      '',
                                      'x= 0 ',
                                      '#' * 125,
                                      'y ',
                                      'puts x'])
          create_file('example2.rb', <<~RUBY)
            # frozen_string_literal: true

            module M1::M2
              class C # :nodoc:
                def m
                  puts '!'
                end
              end
            end
          RUBY
          create_file('.rubocop_todo.yml', <<~YAML)
            Layout/LineLength:
              Enabled: false
          YAML
          create_empty_file('other.yml')
          create_file('.rubocop.yml', code)
          expect(cli.run(['--auto-gen-config'])).to eq(0)
          expect(File.readlines('.rubocop_todo.yml')[8..].map(&:chomp))
            .to eq(['# Offense count: 1',
                    '# This cop supports safe autocorrection (--autocorrect).',
                    '# Configuration parameters: AllowForAlignment, ' \
                    'EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals.',
                    '# SupportedStylesForExponentOperator: space, no_space',
                    '# SupportedStylesForRationalLiterals: space, no_space',
                    'Layout/SpaceAroundOperators:',
                    '  Exclude:',
                    "    - 'example1.rb'",
                    '',
                    '# Offense count: 2',
                    '# This cop supports safe autocorrection (--autocorrect).',
                    '# Configuration parameters: AllowInHeredoc.',
                    'Layout/TrailingWhitespace:',
                    '  Exclude:',
                    "    - 'example1.rb'",
                    '',
                    '# Offense count: 1',
                    '# This cop supports unsafe autocorrection (--autocorrect-all).',
                    '# Configuration parameters: EnforcedStyle, EnforcedStyleForClasses, ' \
                    'EnforcedStyleForModules.',
                    '# SupportedStyles: nested, compact',
                    '# SupportedStylesForClasses: ~, nested, compact',
                    '# SupportedStylesForModules: ~, nested, compact',
                    'Style/ClassAndModuleChildren:',
                    '  Exclude:',
                    "    - 'example2.rb'",
                    '',
                    '# Offense count: 1',
                    '# This cop supports safe autocorrection (--autocorrect).',
                    '# Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, ' \
                    'URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, ' \
                    'AllowedPatterns, SplitStrings.',
                    '# URISchemes: http, https',
                    'Layout/LineLength:',
                    '  Max: 125'])
          expect(File.readlines('.rubocop.yml').map(&:chomp)).to eq(code)

          # Create new CLI instance to avoid using cached configuration.
          new_cli = RuboCop::CLI.new

          expect(new_cli.run(['example1.rb'])).to eq(0)
        end
      end
    end

    it_behaves_like 'overwrites todo file',
                    'only the inherit_from line',
                    ['inherit_from: .rubocop_todo.yml']

    # Makes the shared example work the same with the conditional as without.
    ENV['HZPKCEAXTFQLOWB'] = 'true'

    it_behaves_like 'overwrites todo file',
                    'a single line inherit_from in an ERB conditional',
                    ['<% if ENV["HZPKCEAXTFQLOWB"] %>',
                     'inherit_from: .rubocop_todo.yml',
                     '<% end %>']

    it_behaves_like 'overwrites todo file',
                    'a multiline inherit_from in an ERB conditional',
                    ['<% if ENV["HZPKCEAXTFQLOWB"] %>',
                     'inherit_from:',
                     '  - .rubocop_todo.yml',
                     '  - other.yml',
                     '<% end %>']

    it 'honors rubocop:disable comments' do
      create_file('example1.rb', ['#' * 121,
                                  '# rubocop:disable LineLength',
                                  '#' * 125,
                                  'y ',
                                  'puts 123456',
                                  '# rubocop:enable LineLength'])
      create_file('.rubocop.yml', ['inherit_from: .rubocop_todo.yml'])
      create_file('.rubocop_todo.yml', [''])
      expect(cli.run(['--auto-gen-config'])).to eq(0)
      expect(File.readlines('.rubocop_todo.yml')[8..].join)
        .to eq(['# Offense count: 1',
                '# This cop supports safe autocorrection (--autocorrect).',
                '# Configuration parameters: AllowInHeredoc.',
                'Layout/TrailingWhitespace:',
                '  Exclude:',
                "    - 'example1.rb'",
                '',
                '# Offense count: 2',
                '# This cop supports safe autocorrection (--autocorrect).',
                'Migration/DepartmentName:',
                '  Exclude:',
                "    - 'example1.rb'",
                '',
                '# Offense count: 1',
                '# This cop supports unsafe autocorrection (--autocorrect-all).',
                '# Configuration parameters: EnforcedStyle.',
                '# SupportedStyles: always, always_true, never',
                'Style/FrozenStringLiteralComment:',
                '  Exclude:',
                "    - '**/*.arb'",
                "    - 'example1.rb'",
                '',
                '# Offense count: 1',
                '# This cop supports safe autocorrection (--autocorrect).',
                '# Configuration parameters: Strict, AllowedNumbers, AllowedPatterns.',
                'Style/NumericLiterals:',
                '  MinDigits: 7',
                '',
                '# Offense count: 1',
                '# This cop supports safe autocorrection (--autocorrect).',
                '# Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, ' \
                'URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, ' \
                'AllowedPatterns, SplitStrings.',
                '# URISchemes: http, https',
                'Layout/LineLength:',
                '  Max: 121',
                ''].join("\n"))
    end

    context 'when --only is used' do
      before do
        create_file('example.rb', <<~RUBY)
          # frozen_string_literal: true

          class MyClass
            def initialize
              p "No documentation for class"
            end
          end

          def f
          #{'  #' * 46}
            if #{'a' * 120}
              return y
            end

            z
          end
        RUBY
      end

      context 'when --only does not contain Layout/LineLength' do
        it 'generates TODO only for the mentioned cop' do
          $stdout = StringIO.new
          expect(cli.run(['--auto-gen-config', '--only', 'Style/Documentation'])).to eq(0)
          expect(File.readlines('.rubocop_todo.yml')
                    .drop_while { |line| line.start_with?('#') }.join)
            .to eq(<<~YAML)

              # Offense count: 1
              # Configuration parameters: AllowedConstants.
              Style/Documentation:
                Exclude:
                  - 'spec/**/*'
                  - 'test/**/*'
                  - 'example.rb'
          YAML
          expect($stderr.string).to eq('')
          expect($stdout.string).to eq(<<~STRING)
            Added inheritance from `.rubocop_todo.yml` in `.rubocop.yml`.
            Phase 1 of 2: run Layout/LineLength cop (skipped because a list of cops is passed to the `--only` flag)
            Phase 2 of 2: run all cops
            Inspecting 1 file
            C

            1 file inspected, 1 offense detected
            Created .rubocop_todo.yml.
          STRING
        end
      end

      context 'when --only contains Layout/LineLength' do
        it 'generates TODO for every cop listed in the --only flag' do
          $stdout = StringIO.new
          expect(cli.run(['--auto-gen-config', '--only', 'Layout/LineLength,Style/Documentation']))
            .to eq(0)
          expect(File.readlines('.rubocop_todo.yml')
                    .drop_while { |line| line.start_with?('#') }.join)
            .to eq(<<~YAML)

              # Offense count: 2
              # This cop supports safe autocorrection (--autocorrect).
              # Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
              # URISchemes: http, https
              Layout/LineLength:
                Max: 138

              # Offense count: 1
              # Configuration parameters: AllowedConstants.
              Style/Documentation:
                Exclude:
                  - 'spec/**/*'
                  - 'test/**/*'
                  - 'example.rb'
          YAML
          expect($stderr.string).to eq('')
          expect($stdout.string).to eq(<<~STRING)
            Added inheritance from `.rubocop_todo.yml` in `.rubocop.yml`.
            Phase 1 of 2: run Layout/LineLength cop (skipped because a list of cops is passed to the `--only` flag)
            Phase 2 of 2: run all cops
            Inspecting 1 file
            C

            1 file inspected, 3 offenses detected
            Created .rubocop_todo.yml.
          STRING
        end
      end
    end

    context 'when --config is used' do
      it 'can generate a todo list' do
        create_file('example1.rb', ['$x = 0 ', '#' * 90, 'y ', 'puts x'])
        create_file('dir/cop_config.yml', <<~YAML)
          Layout/TrailingWhitespace:
            Enabled: false
          Layout/LineLength:
            Max: 95
        YAML
        expect(cli.run(%w[--auto-gen-config --config dir/cop_config.yml])).to eq(0)
        expect(Dir['.*']).to include('.rubocop_todo.yml')
        todo_contents = File.read('.rubocop_todo.yml').lines[8..].join
        expect(todo_contents).to eq(<<~YAML)
          # Offense count: 1
          # This cop supports unsafe autocorrection (--autocorrect-all).
          # Configuration parameters: EnforcedStyle.
          # SupportedStyles: always, always_true, never
          Style/FrozenStringLiteralComment:
            Exclude:
              - '**/*.arb'
              - 'example1.rb'

          # Offense count: 1
          # Configuration parameters: AllowedVariables.
          Style/GlobalVars:
            Exclude:
              - 'example1.rb'
        YAML
        expect(File.read('dir/cop_config.yml')).to eq(<<~YAML)
          inherit_from: ../.rubocop_todo.yml

          Layout/TrailingWhitespace:
            Enabled: false
          Layout/LineLength:
            Max: 95
        YAML
        # Checks that the command can be run again with config modified by itself.
        expect(cli.run(%w[--auto-gen-config --config dir/cop_config.yml])).to eq(0)
      end

      context 'when --config is used with an absolute path' do
        it 'can generate a todo list' do
          create_file('example1.rb', ['$x = 0 ', '#' * 90, 'y ', 'puts x'])
          create_empty_file('.rubocop.yml')
          config_path = File.absolute_path('.rubocop.yml')
          expect(cli.run(['--auto-gen-config', '--config', config_path])).to eq(0)
          expect(Dir['.*']).to include('.rubocop_todo.yml')

          # Check that paths are relativised correctly
          expect(File.read('.rubocop_todo.yml')).to include(" - 'example1.rb'")
          expect(File.read('.rubocop.yml')).to include('inherit_from: .rubocop_todo.yml')
          # Checks that the command can be run again with config modified by itself.
          expect(cli.run(['--auto-gen-config', '--config', config_path])).to eq(0)
        end
      end

      it 'can generate a todo list if default .rubocop.yml exists' do
        create_file('example1.rb', ['def foo', '  # bar', '  end'])
        create_file('.rubocop.yml', <<~YAML)
          AllCops:
            DisabledByDefault: true

          Layout/DefEndAlignment:
            Enabled: true
        YAML
        create_empty_file('cop_config.yml')

        expect(cli.run(%w[--auto-gen-config --config cop_config.yml])).to eq(0)
        expect(Dir['.*']).to include('.rubocop_todo.yml')
        todo_contents = File.read('.rubocop_todo.yml').lines[8..].join
        expect(todo_contents).to eq(<<~YAML)
          # Offense count: 1
          # This cop supports safe autocorrection (--autocorrect).
          # Configuration parameters: AllowForAlignment, IndentationWidth.
          Layout/CommentIndentation:
            Exclude:
              - 'example1.rb'

          # Offense count: 1
          # This cop supports safe autocorrection (--autocorrect).
          # Configuration parameters: EnforcedStyleAlignWith.
          # SupportedStylesAlignWith: start_of_line, def
          Layout/DefEndAlignment:
            Exclude:
              - 'example1.rb'

          # Offense count: 1
          # This cop supports unsafe autocorrection (--autocorrect-all).
          # Configuration parameters: EnforcedStyle.
          # SupportedStyles: always, always_true, never
          Style/FrozenStringLiteralComment:
            Exclude:
              - '**/*.arb'
              - 'example1.rb'
        YAML
        expect(File.read('cop_config.yml')).to eq(<<~YAML)
          inherit_from: .rubocop_todo.yml
        YAML
      end
    end

    context 'when working with a cop who do not support autocorrection' do
      it 'can generate a todo list' do
        create_file('example1.rb', <<~RUBY)
          def fooBar; end
        RUBY
        create_file('.rubocop.yml', <<~YAML)
          # The following cop does not support autocorrection.
          Naming/MethodName:
            Enabled: true
        YAML
        expect(cli.run(%w[--auto-gen-config])).to eq(0)
        expect($stderr.string).to eq('')
        # expect($stdout.string).to include('Created .rubocop_todo.yml.')
        expect(Dir['.*']).to include('.rubocop_todo.yml')
        todo_contents = File.read('.rubocop_todo.yml').lines[8..].join
        expect(todo_contents).to eq(<<~YAML)
          # Offense count: 1
          # Configuration parameters: AllowedPatterns, ForbiddenIdentifiers, ForbiddenPatterns.
          # SupportedStyles: snake_case, camelCase
          # ForbiddenIdentifiers: __id__, __send__
          Naming/MethodName:
            EnforcedStyle: camelCase

          # Offense count: 1
          # This cop supports unsafe autocorrection (--autocorrect-all).
          # Configuration parameters: EnforcedStyle.
          # SupportedStyles: always, always_true, never
          Style/FrozenStringLiteralComment:
            Exclude:
              - '**/*.arb'
              - 'example1.rb'
        YAML
        expect(File.read('.rubocop.yml')).to eq(<<~YAML)
          inherit_from: .rubocop_todo.yml

          # The following cop does not support autocorrection.
          Naming/MethodName:
            Enabled: true
        YAML
      end
    end

    context 'when cop is not safe to autocorrect' do
      it 'can generate a todo list, with the appropriate flag' do
        create_file('example1.rb', <<~RUBY)
          # frozen_string_literal: true

          users = (user.name + ' ' + user.email) * 5
          puts users
        RUBY
        create_file('.rubocop.yml', <<~YAML)
          # The following cop supports autocorrection but is not safe
          Style/StringConcatenation:
            Enabled: true
        YAML
        expect(cli.run(%w[--auto-gen-config])).to eq(0)
        expect($stderr.string).to eq('')
        expect(Dir['.*']).to include('.rubocop_todo.yml')
        todo_contents = File.read('.rubocop_todo.yml').lines[8..].join
        expect(todo_contents).to eq(<<~YAML)
          # Offense count: 1
          # This cop supports unsafe autocorrection (--autocorrect-all).
          # Configuration parameters: Mode.
          Style/StringConcatenation:
            Exclude:
              - 'example1.rb'
        YAML
        expect(File.read('.rubocop.yml')).to eq(<<~YAML)
          inherit_from: .rubocop_todo.yml

          # The following cop supports autocorrection but is not safe
          Style/StringConcatenation:
            Enabled: true
        YAML
      end
    end

    context 'when existing config file has a YAML document start header' do
      it 'inserts `inherit_from` key after header' do
        create_file('example1.rb', <<~RUBY)
          def foo; end
        RUBY
        create_file('.rubocop.yml', <<~YAML)
          # rubocop config file
          ---  # YAML document start
          # The following cop does not support autocorrection.
          Naming/MethodName:
            Enabled: true
        YAML
        expect(cli.run(%w[--auto-gen-config])).to eq(0)
        expect($stderr.string).to eq('')
        expect(Dir['.*']).to include('.rubocop_todo.yml')
        todo_contents = File.read('.rubocop_todo.yml').lines[8..].join
        expect(todo_contents).to eq(<<~YAML)
          # Offense count: 1
          # This cop supports unsafe autocorrection (--autocorrect-all).
          # Configuration parameters: EnforcedStyle.
          # SupportedStyles: always, always_true, never
          Style/FrozenStringLiteralComment:
            Exclude:
              - '**/*.arb'
              - 'example1.rb'
        YAML
        expect(File.read('.rubocop.yml')).to eq(<<~YAML)
          # rubocop config file
          ---  # YAML document start
          inherit_from: .rubocop_todo.yml

          # The following cop does not support autocorrection.
          Naming/MethodName:
            Enabled: true
        YAML
      end
    end

    context 'when working in a subdirectory' do
      it 'can generate a todo list' do
        create_file('dir/example1.rb', ['$x = 0 ', '#' * 90, 'y ', 'puts x'])
        create_file('dir/.rubocop.yml', <<~YAML)
          inherit_from: ../.rubocop.yml
        YAML
        create_file('.rubocop.yml', <<~YAML)
          Layout/TrailingWhitespace:
            Enabled: false
          Layout/LineLength:
            Max: 95
        YAML
        Dir.chdir('dir') { expect(cli.run(%w[--auto-gen-config])).to eq(0) }
        expect($stderr.string).to eq('')
        # expect($stdout.string).to include('Created .rubocop_todo.yml.')
        expect(Dir['dir/.*']).to include('dir/.rubocop_todo.yml')
        todo_contents = File.read('dir/.rubocop_todo.yml').lines[8..].join
        expect(todo_contents).to eq(<<~YAML)
          # Offense count: 1
          # This cop supports unsafe autocorrection (--autocorrect-all).
          # Configuration parameters: EnforcedStyle.
          # SupportedStyles: always, always_true, never
          Style/FrozenStringLiteralComment:
            Exclude:
              - '**/*.arb'
              - 'example1.rb'

          # Offense count: 1
          # Configuration parameters: AllowedVariables.
          Style/GlobalVars:
            Exclude:
              - 'example1.rb'
        YAML
        expect(File.read('dir/.rubocop.yml')).to eq(<<~YAML)
          inherit_from:
            - .rubocop_todo.yml
            - ../.rubocop.yml
        YAML
      end
    end

    context 'when inheriting from a URL' do
      let(:remote_config_url) { 'https://example.com/foo/bar' }

      before do
        stub_request(:get, remote_config_url)
          .to_return(status: 200, body: "Style/Encoding:\n    Enabled: true")
      end

      context 'when there is a single entry' do
        it 'can generate a todo list' do
          create_file('dir/example1.rb', ['$x = 0 ', '#' * 90, 'y ', 'puts x'])
          create_file('.rubocop.yml', <<~YAML)
            inherit_from: #{remote_config_url}
          YAML
          expect(cli.run(%w[--auto-gen-config])).to eq(0)
          expect($stderr.string).to eq('')
          expect($stdout.string).to include(<<~YAML)
            Added inheritance from `.rubocop_todo.yml` in `.rubocop.yml`.
          YAML
          expect(File.read('.rubocop.yml')).to eq(<<~YAML)
            inherit_from:
              - .rubocop_todo.yml
              - #{remote_config_url}
          YAML
        end
      end

      context 'when there are multiple entries' do
        it 'can generate a todo list' do
          create_file('dir/example1.rb', ['$x = 0 ', '#' * 90, 'y ', 'puts x'])
          create_file('.rubocop.yml', <<~YAML)
            inherit_from:
              - #{remote_config_url}
          YAML
          expect(cli.run(%w[--auto-gen-config])).to eq(0)
          expect($stderr.string).to eq('')
          expect($stdout.string).to include(<<~YAML)
            Added inheritance from `.rubocop_todo.yml` in `.rubocop.yml`.
          YAML
          expect(File.read('.rubocop.yml')).to eq(<<~YAML)
            inherit_from:
              - .rubocop_todo.yml
              - #{remote_config_url}
          YAML
        end
      end
    end

    it 'can generate a todo list' do
      create_file('example1.rb', ['# frozen_string_literal: true',
                                  '',
                                  '$x= 0 ',
                                  '#' * 130,
                                  '#' * 125,
                                  'y ',
                                  'puts x'])
      create_file('example2.rb', <<~RUBY)
        # frozen_string_literal: true

        \tx = 0
        puts x

        class A
          def a; end
        end
      RUBY
      # Make ConfigLoader reload the default configuration so that its
      # absolute Exclude paths will point into this example's work directory.
      RuboCop::ConfigLoader.default_configuration = nil

      expect(cli.run(['--auto-gen-config'])).to eq(0)
      expect($stderr.string).to eq('')
      expect($stdout.string).to include('Created .rubocop_todo.yml.')
      expected =
        ['# This configuration was generated by',
         '# `rubocop --auto-gen-config`',
         /# on .* using RuboCop version .*/,
         '# The point is for the user to remove these configuration records',
         '# one by one as the offenses are removed from the code base.',
         '# Note that changes in the inspected code, or installation of new',
         '# versions of RuboCop, may require this file to be generated ' \
         'again.',
         '',
         '# Offense count: 1',
         '# This cop supports safe autocorrection (--autocorrect).',
         '# Configuration parameters: AllowForAlignment, IndentationWidth.',
         'Layout/CommentIndentation:',
         '  Exclude:',
         "    - 'example2.rb'",
         '',
         '# Offense count: 2',
         '# This cop supports safe autocorrection (--autocorrect).',
         '# Configuration parameters: EnforcedStyle.',
         '# SupportedStyles: normal, indented_internal_methods',
         'Layout/IndentationConsistency:',
         '  Exclude:',
         "    - 'example2.rb'",
         '',
         '# Offense count: 1',
         '# This cop supports safe autocorrection (--autocorrect).',
         '# Configuration parameters: IndentationWidth, EnforcedStyle.',
         '# SupportedStyles: spaces, tabs',
         'Layout/IndentationStyle:',
         '  Exclude:',
         "    - 'example2.rb'",
         '',
         '# Offense count: 1',
         '# This cop supports safe autocorrection (--autocorrect).',
         'Layout/InitialIndentation:',
         '  Exclude:',
         "    - 'example2.rb'",
         '',
         '# Offense count: 1',
         '# This cop supports safe autocorrection (--autocorrect).',
         '# Configuration parameters: AllowForAlignment, ' \
         'EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals.',
         '# SupportedStylesForExponentOperator: space, no_space',
         '# SupportedStylesForRationalLiterals: space, no_space',
         'Layout/SpaceAroundOperators:',
         '  Exclude:',
         "    - 'example1.rb'",
         '',
         '# Offense count: 2',
         '# This cop supports safe autocorrection (--autocorrect).',
         '# Configuration parameters: AllowInHeredoc.',
         'Layout/TrailingWhitespace:',
         '  Exclude:',
         "    - 'example1.rb'",
         '',
         '# Offense count: 1',
         '# Configuration parameters: AllowedConstants.',
         'Style/Documentation:',
         '  Exclude:',
         "    - 'spec/**/*'", # Copied from default configuration
         "    - 'test/**/*'", # Copied from default configuration
         "    - 'example2.rb'",
         '',
         '# Offense count: 1',
         '# Configuration parameters: AllowedVariables.',
         'Style/GlobalVars:',
         '  Exclude:',
         "    - 'example1.rb'",
         '',
         '# Offense count: 2',
         '# This cop supports safe autocorrection (--autocorrect).',
         '# Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, ' \
         'AllowRBSInlineAnnotation, AllowCopDirectives, ' \
         'AllowedPatterns, SplitStrings.',
         '# URISchemes: http, https',
         'Layout/LineLength:',
         '  Max: 130']
      actual = File.read('.rubocop_todo.yml').split($RS)
      expected.each_with_index do |line, ix|
        if line.is_a?(String)
          expect(actual[ix]).to eq(line)
        else
          expect(actual[ix]).to match(line)
        end
      end
      expect(actual.size).to eq(expected.size)
    end

    it 'can generate Exclude properties with a given limit' do
      create_file('example1.rb', ['# frozen_string_literal: true',
                                  '',
                                  '$x= 0 ',
                                  '#' * 130,
                                  '#' * 125,
                                  'y ',
                                  'puts x'])
      create_file('example2.rb', ['# frozen_string_literal: true',
                                  '',
                                  '#' * 125,
                                  "\tx = 0",
                                  'puts x '])
      expect(cli.run(['--auto-gen-config', '--exclude-limit', '1'])).to eq(0)
      expected =
        ['# This configuration was generated by',
         '# `rubocop --auto-gen-config --exclude-limit 1`',
         /# on .* using RuboCop version .*/,
         '# The point is for the user to remove these configuration records',
         '# one by one as the offenses are removed from the code base.',
         '# Note that changes in the inspected code, or installation of new',
         '# versions of RuboCop, may require this file to be generated ' \
         'again.',
         '',
         '# Offense count: 1',
         '# This cop supports safe autocorrection (--autocorrect).',
         '# Configuration parameters: AllowForAlignment, IndentationWidth.',
         'Layout/CommentIndentation:',
         '  Exclude:',
         "    - 'example2.rb'",
         '',
         '# Offense count: 1',
         '# This cop supports safe autocorrection (--autocorrect).',
         '# Configuration parameters: EnforcedStyle.',
         '# SupportedStyles: normal, indented_internal_methods',
         'Layout/IndentationConsistency:',
         '  Exclude:',
         "    - 'example2.rb'",
         '',
         '# Offense count: 1',
         '# This cop supports safe autocorrection (--autocorrect).',
         '# Configuration parameters: IndentationWidth, EnforcedStyle.',
         '# SupportedStyles: spaces, tabs',
         'Layout/IndentationStyle:',
         '  Exclude:',
         "    - 'example2.rb'",
         '',
         '# Offense count: 1',
         '# This cop supports safe autocorrection (--autocorrect).',
         'Layout/InitialIndentation:',
         '  Exclude:',
         "    - 'example2.rb'",
         '',
         '# Offense count: 1',
         '# This cop supports safe autocorrection (--autocorrect).',
         '# Configuration parameters: AllowForAlignment, ' \
         'EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals.',
         '# SupportedStylesForExponentOperator: space, no_space',
         '# SupportedStylesForRationalLiterals: space, no_space',
         'Layout/SpaceAroundOperators:',
         '  Exclude:',
         "    - 'example1.rb'",
         '',
         '# Offense count: 3',
         '# This cop supports safe autocorrection (--autocorrect).',
         '# Configuration parameters: AllowInHeredoc.',
         'Layout/TrailingWhitespace:',
         '  Enabled: false', # Offenses in 2 files, limit is 1, so no Exclude
         '',
         '# Offense count: 1',
         '# Configuration parameters: AllowedVariables.',
         'Style/GlobalVars:',
         '  Exclude:',
         "    - 'example1.rb'",
         '',
         '# Offense count: 3',
         '# This cop supports safe autocorrection (--autocorrect).',
         '# Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, ' \
         'AllowRBSInlineAnnotation, AllowCopDirectives, ' \
         'AllowedPatterns, SplitStrings.',
         '# URISchemes: http, https',
         'Layout/LineLength:',
         '  Max: 130']
      actual = File.read('.rubocop_todo.yml').split($RS)
      expected.each_with_index do |line, ix|
        if line.is_a?(String)
          expect(actual[ix]).to eq(line)
        else
          expect(actual[ix]).to match(line)
        end
      end
      expect(actual.size).to eq(expected.size)
    end

    context 'for existing configuration with Exclude' do
      before do
        create_file('example1.rb', ['# frozen_string_literal: true', '', 'y '])
        create_file('example2.rb', ['# frozen_string_literal: true', '', 'x = 0 ', 'puts x'])
      end

      it 'generates Excludes that appear in .rubocop.yml' do
        create_file('.rubocop.yml', <<~YAML)
          Layout/TrailingWhitespace:
            Exclude:
              - 'example1.rb'
        YAML
        expect(cli.run(['--auto-gen-config'])).to eq(0)
        expect($stderr.string.chomp)
          .to eq('`Layout/TrailingWhitespace: Exclude` in `.rubocop.yml` overrides a generated ' \
                 '`Exclude` in `.rubocop_todo.yml`. Either remove ' \
                 '`Layout/TrailingWhitespace: Exclude` or add `inherit_mode: merge: - Exclude`.')
        expected = <<~YAML
          Layout/TrailingWhitespace:
            Exclude:
              - 'example1.rb'
              - 'example2.rb'
        YAML
        actual = File.read('.rubocop_todo.yml').lines.grep_v(/^(#.*)?$/)
        expect(actual.join).to eq(expected)

        $stdout = StringIO.new
        expect(cli.run(['--format', 'offenses'])).to eq(1)
        expect($stdout.string.lines.grep(%r{/})).to eq(
          ["1  Layout/TrailingWhitespace [Safe Correctable]\n"]
        )
      end

      shared_examples 'leaves out Excludes' do |merge_style, config|
        it "leaves out Excludes that appear in .rubocop.yml but are merged #{merge_style}" do
          create_file('.rubocop.yml', config)
          expect(cli.run(['--auto-gen-config'])).to eq(0)
          expect($stderr.string).to eq('')
          expected = <<~YAML
            Layout/TrailingWhitespace:
              Exclude:
                - 'example2.rb'
          YAML
          actual = File.read('.rubocop_todo.yml').lines.grep_v(/^(#.*)?$/)
          expect(actual.join).to eq(expected)

          # NOTE: Reload CLI to ensure updated config with rubocop_todo is properly picked up.
          #       ConfigStore#for_pwd caches results; re-create CLI to avoid using stale config.
          fresh_cli = RuboCop::CLI.new
          expect(fresh_cli.run([])).to eq(0)
        end
      end

      it_behaves_like 'leaves out Excludes', 'globally', <<~YAML
        inherit_mode:
          merge:
            - Exclude

        Layout/TrailingWhitespace:
          Exclude:
            - 'example1.rb'
      YAML
      it_behaves_like 'leaves out Excludes', 'for the cop', <<~YAML
        Layout/TrailingWhitespace:
          inherit_mode:
            merge:
              - Exclude

          Exclude:
            - 'example1.rb'
      YAML
    end

    context 'when duplicated default configuration parameter' do
      before do
        RuboCop::ConfigLoader.default_configuration['Naming/MethodParameterName']
                             .merge!('AllowedNames' => %w[at by at])
      end

      it 'parameters are displayed without duplication' do
        create_file('.rubocop.yml', <<~YAML)
          Naming/VariableName:
            Enabled: false
        YAML
        create_file('example1.rb', <<~TEXT)
          # frozen_string_literal: true

          def bar(varOne, varTwo)
            varOne + varTwo
          end
        TEXT

        expect(cli.run(['--auto-gen-config'])).to eq(0)
        File.readlines('.rubocop_todo.yml')
        expect(File.readlines('.rubocop_todo.yml')[9..].join)
          .to eq(<<~YAML)
            # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
            # AllowedNames: at, by
            Naming/MethodParameterName:
              Exclude:
                - 'example1.rb'
          YAML
      end
    end

    it 'does not generate configuration for the Syntax cop' do
      create_file('example1.rb', <<~RUBY)
        # frozen_string_literal: true

        x = <  # Syntax error
        puts x
      RUBY
      create_file('example2.rb', <<~RUBY)
        # frozen_string_literal: true

        \tx = 0
        puts x
      RUBY
      expect(cli.run(['--auto-gen-config'])).to eq(0)
      expect($stderr.string).to eq('')
      actual = File.read('.rubocop_todo.yml').split($RS)
      date_stamp = actual.slice!(2)
      expect(date_stamp).to match(/# on .* using RuboCop version .*/)
      expect(actual.join("\n")).to eq(<<~TEXT.chomp)
        # This configuration was generated by
        # `rubocop --auto-gen-config`
        # The point is for the user to remove these configuration records
        # one by one as the offenses are removed from the code base.
        # Note that changes in the inspected code, or installation of new
        # versions of RuboCop, may require this file to be generated again.

        # Offense count: 1
        # This cop supports safe autocorrection (--autocorrect).
        # Configuration parameters: AllowForAlignment, IndentationWidth.
        Layout/CommentIndentation:
          Exclude:
            - 'example2.rb'

        # Offense count: 1
        # This cop supports safe autocorrection (--autocorrect).
        # Configuration parameters: EnforcedStyle.
        # SupportedStyles: normal, indented_internal_methods
        Layout/IndentationConsistency:
          Exclude:
            - 'example2.rb'

        # Offense count: 1
        # This cop supports safe autocorrection (--autocorrect).
        # Configuration parameters: IndentationWidth, EnforcedStyle.
        # SupportedStyles: spaces, tabs
        Layout/IndentationStyle:
          Exclude:
            - 'example2.rb'

        # Offense count: 1
        # This cop supports safe autocorrection (--autocorrect).
        Layout/InitialIndentation:
          Exclude:
            - 'example2.rb'
      TEXT
    end

    it 'generates a todo list that removes the reports' do
      create_file('example.rb', ['# frozen_string_literal: true', '', 'y.gsub!(/abc\/xyz/, x)'])
      expect(cli.run(%w[--format emacs])).to eq(1)
      expect($stdout.string).to eq(
        "#{abs('example.rb')}:3:9: C: [Correctable] Style/RegexpLiteral: Use `%r` " \
        "around regular expression.\n"
      )
      expect(cli.run(['--auto-gen-config'])).to eq(0)
      expected =
        ['# This configuration was generated by',
         '# `rubocop --auto-gen-config`',
         /# on .* using RuboCop version .*/,
         '# The point is for the user to remove these configuration records',
         '# one by one as the offenses are removed from the code base.',
         '# Note that changes in the inspected code, or installation of new',
         '# versions of RuboCop, may require this file to be generated ' \
         'again.',
         '',
         '# Offense count: 1',
         '# This cop supports safe autocorrection (--autocorrect).',
         '# Configuration parameters: EnforcedStyle, AllowInnerSlashes.',
         '# SupportedStyles: slashes, percent_r, mixed',
         'Style/RegexpLiteral:',
         '  Exclude:',
         "    - 'example.rb'"]
      actual = File.read('.rubocop_todo.yml').split($RS)
      expected.each_with_index do |line, ix|
        if line.is_a?(String)
          expect(actual[ix]).to eq(line)
        else
          expect(actual[ix]).to match(line)
        end
      end
      expect(actual.size).to eq(expected.size)
      $stdout = StringIO.new
      result = cli.run(%w[--config .rubocop_todo.yml --format emacs])
      expect($stdout.string).to eq('')
      expect(result).to eq(0)
    end

    it 'does not include offense counts when --no-offense-counts is used' do
      create_file('example1.rb', ['# frozen_string_literal: true',
                                  '',
                                  '$x= 0 ',
                                  '#' * 130,
                                  '#' * 125,
                                  'y ',
                                  'puts x'])
      create_file('example2.rb', <<~RUBY)
        # frozen_string_literal: true

        \tx = 0
        puts x

        class A
          def a; end
        end
      RUBY
      # Make ConfigLoader reload the default configuration so that its
      # absolute Exclude paths will point into this example's work directory.
      RuboCop::ConfigLoader.default_configuration = nil

      expect(cli.run(['--auto-gen-config', '--no-offense-counts'])).to eq(0)
      expect($stderr.string).to eq('')
      expect($stdout.string).to include('Created .rubocop_todo.yml.')
      expected =
        ['# This configuration was generated by',
         '# `rubocop --auto-gen-config --no-offense-counts`',
         /# on .* using RuboCop version .*/,
         '# The point is for the user to remove these configuration records',
         '# one by one as the offenses are removed from the code base.',
         '# Note that changes in the inspected code, or installation of new',
         '# versions of RuboCop, may require this file to be generated ' \
         'again.',
         '',
         '# This cop supports safe autocorrection (--autocorrect).',
         '# Configuration parameters: AllowForAlignment, IndentationWidth.',
         'Layout/CommentIndentation:',
         '  Exclude:',
         "    - 'example2.rb'",
         '',
         '# This cop supports safe autocorrection (--autocorrect).',
         '# Configuration parameters: EnforcedStyle.',
         '# SupportedStyles: normal, indented_internal_methods',
         'Layout/IndentationConsistency:',
         '  Exclude:',
         "    - 'example2.rb'",
         '',
         '# This cop supports safe autocorrection (--autocorrect).',
         '# Configuration parameters: IndentationWidth, EnforcedStyle.',
         '# SupportedStyles: spaces, tabs',
         'Layout/IndentationStyle:',
         '  Exclude:',
         "    - 'example2.rb'",
         '',
         '# This cop supports safe autocorrection (--autocorrect).',
         'Layout/InitialIndentation:',
         '  Exclude:',
         "    - 'example2.rb'",
         '',
         '# This cop supports safe autocorrection (--autocorrect).',
         '# Configuration parameters: AllowForAlignment, ' \
         'EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals.',
         '# SupportedStylesForExponentOperator: space, no_space',
         '# SupportedStylesForRationalLiterals: space, no_space',
         'Layout/SpaceAroundOperators:',
         '  Exclude:',
         "    - 'example1.rb'",
         '',
         '# This cop supports safe autocorrection (--autocorrect).',
         '# Configuration parameters: AllowInHeredoc.',
         'Layout/TrailingWhitespace:',
         '  Exclude:',
         "    - 'example1.rb'",
         '',
         '# Configuration parameters: AllowedConstants.',
         'Style/Documentation:',
         '  Exclude:',
         "    - 'spec/**/*'", # Copied from default configuration
         "    - 'test/**/*'", # Copied from default configuration
         "    - 'example2.rb'",
         '',
         '# Configuration parameters: AllowedVariables.',
         'Style/GlobalVars:',
         '  Exclude:',
         "    - 'example1.rb'",
         '',
         '# This cop supports safe autocorrection (--autocorrect).',
         '# Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, ' \
         'AllowRBSInlineAnnotation, AllowCopDirectives, ' \
         'AllowedPatterns, SplitStrings.',
         '# URISchemes: http, https',
         'Layout/LineLength:',
         '  Max: 130']
      actual = File.read('.rubocop_todo.yml').split($RS)
      expected.each_with_index do |line, ix|
        if line.is_a?(String)
          expect(actual[ix]).to eq(line)
        else
          expect(actual[ix]).to match(line)
        end
      end
      expect(actual.size).to eq(expected.size)
    end

    it 'generates Exclude instead of Max when --auto-gen-only-exclude is used' do
      create_file('example1.rb', ['#' * 130, '#' * 130, 'puts 123456'])
      create_file('example2.rb', <<~RUBY)
        def function(arg1, arg2, arg3, arg4, arg5, arg6, arg7)
          puts 123456
        end
      RUBY
      # Make ConfigLoader reload the default configuration so that its
      # absolute Exclude paths will point into this example's work directory.
      RuboCop::ConfigLoader.default_configuration = nil

      $stdout = StringIO.new
      expect(cli.run(['--auto-gen-config', '--auto-gen-only-exclude',
                      '--exclude-limit', '1'])).to eq(0)
      expect($stdout.string).to include(<<~STRING)
        Phase 1 of 2: run Layout/LineLength cop (skipped because only excludes will be generated due to `--auto-gen-only-exclude` flag)
        Phase 2 of 2: run all cops
      STRING
      actual = File.read('.rubocop_todo.yml').split($RS)

      # With --exclude-limit 1 we get MinDigits generated for NumericLiterals
      # because there's one offense in each file. The other cops have offenses
      # in just one file, even though there may be more than one offense for
      # the same cop in a single file. Exclude properties are generated for
      # them.
      expect(actual.grep(/^[^#]/).join($RS)).to eq(<<~YAML.chomp)
        Layout/LineLength:
          Exclude:
            - 'example1.rb'
        Lint/UnusedMethodArgument:
          Exclude:
            - 'example2.rb'
        Metrics/ParameterLists:
          Exclude:
            - 'example2.rb'
        Style/FrozenStringLiteralComment:
          Enabled: false
        Style/NumericLiterals:
          MinDigits: 7
      YAML
    end

    it 'does not duplicate `Layout/LineLength` with --auto-gen-only-exclude when it is disabled inline and other offense' do
      create_file('example1.rb', <<~RUBY)
        # frozen_string_literal: true

        # Documentation comment
        class Foo
          def foo
            puts #{'a' * 150}
          end

          # rubocop:disable Layout/LineLength
          def bar
            puts #{'a' * 150}
          end
          # rubocop:enable Layout/LineLength
        end
      RUBY

      expect(cli.run(['--auto-gen-config', '--auto-gen-only-exclude'])).to eq(0)
      actual = File.read('.rubocop_todo.yml').split($RS)

      expect(actual.grep(/^[^#]/).join($RS)).to eq(<<~YAML.chomp)
        Layout/LineLength:
          Exclude:
            - 'example1.rb'
      YAML
    end

    it 'includes --auto-gen-only-exclude in the command comment when given' do
      create_file('example1.rb', ['$!'])
      expect(cli.run(['--auto-gen-config', '--auto-gen-only-exclude',
                      '--exclude-limit', '1'])).to eq(0)

      command = '# `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 1`'
      expect(File.readlines('.rubocop_todo.yml')[1].chomp).to eq(command)
    end

    it 'does not include a timestamp when --no-auto-gen-timestamp is used' do
      create_file('example1.rb', ['$!'])
      expect(cli.run(['--auto-gen-config', '--no-auto-gen-timestamp'])).to eq(0)
      expect(File.readlines('.rubocop_todo.yml')[2]).to match(/# using RuboCop version .*/)
    end

    describe 'when --no-exclude-limit is given' do
      let(:offending_files_count) do
        RuboCop::Options::DEFAULT_MAXIMUM_EXCLUSION_ITEMS + 1
      end

      context 'for a cop without Max configuration' do
        before do
          offending_files_count.times do |i|
            create_file("example#{i}.rb", [' '])
          end
        end

        it 'always prefers Exclude to Enabled' do
          expect(cli.run(['--auto-gen-config', '--no-exclude-limit'])).to eq(0)
          lines = File.readlines('.rubocop_todo.yml')
          expect(lines[1]).to eq("# `rubocop --auto-gen-config --no-exclude-limit`\n")
          expect(lines[9..12].join).to eq(
            <<~YAML
              # This cop supports safe autocorrection (--autocorrect).
              # Configuration parameters: AllowInHeredoc.
              Layout/TrailingWhitespace:
                Exclude:
            YAML
          )
          expect(lines[13..]).to eq(
            Array.new(offending_files_count) do |i|
              "    - 'example#{i}.rb'\n"
            end.sort
          )
        end
      end

      context 'for a cop with Max config' do
        before do
          create_file('.rubocop.yml', <<~YAML)
            AllCops:
              DisabledByDefault: true
            Metrics/ClassLength:
              Enabled: true
          YAML
          offending_files_count.times do |i|
            max_lines = RuboCop::ConfigLoader.default_configuration['Metrics/ClassLength']['Max']
            create_file("example#{i}.rb", ['class Foo', *Array.new(max_lines + 1, '  bar'), 'end'])
          end
        end

        it 'always prefers Exclude to Enabled' do
          expect(cli.run(['--auto-gen-config', '--auto-gen-only-exclude',
                          '--no-exclude-limit'])).to eq(0)
          lines = File.readlines('.rubocop_todo.yml')
          expect(lines[9..11].join).to eq(
            <<~YAML
              # Configuration parameters: CountComments, Max, CountAsOne.
              Metrics/ClassLength:
                Exclude:
            YAML
          )
          expect(lines[12..]).to eq(
            Array.new(offending_files_count) do |i|
              "    - 'example#{i}.rb'\n"
            end.sort
          )
        end
      end
    end

    describe 'when different styles appear in different files' do
      before do
        create_file('example1.rb', ['$!'])
        create_file('example2.rb', ['$!'])
        create_file('example3.rb', ['$ERROR_INFO'])
      end

      it 'disables cop if --exclude-limit is exceeded' do
        expect(cli.run(['--auto-gen-config', '--exclude-limit', '1'])).to eq(0)
        expect(File.readlines('.rubocop_todo.yml')[8..].join)
          .to eq(<<~YAML)
            # Offense count: 3
            # This cop supports unsafe autocorrection (--autocorrect-all).
            # Configuration parameters: EnforcedStyle.
            # SupportedStyles: always, always_true, never
            Style/FrozenStringLiteralComment:
              Enabled: false

            # Offense count: 2
            # This cop supports unsafe autocorrection (--autocorrect-all).
            # Configuration parameters: RequireEnglish, EnforcedStyle.
            # SupportedStyles: use_perl_names, use_english_names, use_builtin_english_names
            Style/SpecialGlobalVars:
              Enabled: false
          YAML
      end

      it 'generates Exclude list if --exclude-limit is not exceeded' do
        create_file('example4.rb', ['$!'])
        expect(cli.run(['--auto-gen-config', '--exclude-limit', '10'])).to eq(0)
        expect(File.readlines('.rubocop_todo.yml')[8..].join)
          .to eq(<<~YAML)
            # Offense count: 4
            # This cop supports unsafe autocorrection (--autocorrect-all).
            # Configuration parameters: EnforcedStyle.
            # SupportedStyles: always, always_true, never
            Style/FrozenStringLiteralComment:
              Exclude:
                - '**/*.arb'
                - 'example1.rb'
                - 'example2.rb'
                - 'example3.rb'
                - 'example4.rb'

            # Offense count: 3
            # This cop supports unsafe autocorrection (--autocorrect-all).
            # Configuration parameters: RequireEnglish, EnforcedStyle.
            # SupportedStyles: use_perl_names, use_english_names, use_builtin_english_names
            Style/SpecialGlobalVars:
              Exclude:
                - 'example1.rb'
                - 'example2.rb'
                - 'example4.rb'
          YAML
      end
    end

    describe 'console output' do
      before { create_file('example1.rb', ['# frozen_string_literal: true', '', '$!']) }

      it 'displays report summary but no offenses' do
        expect(cli.run(['--auto-gen-config'])).to eq(0)

        expect($stdout.string).to include(<<~OUTPUT)
          Inspecting 1 file
          C

          1 file inspected, 1 offense detected, 1 offense autocorrectable
          Created .rubocop_todo.yml.
        OUTPUT
      end
    end

    describe 'when `--auto-gen-enforced-style` is given' do
      it 'generates EnforcedStyle parameter if it solves all offenses' do
        create_file('example1.rb', ['# frozen_string_literal: true', '', 'h(:a => 1)'])

        expect(cli.run(['--auto-gen-config', '--auto-gen-enforced-style'])).to eq(0)
        expect(File.readlines('.rubocop_todo.yml')[10..].join)
          .to eq(<<~YAML)
            # Configuration parameters: EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
            # SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
            # SupportedShorthandSyntax: always, never, either, consistent, either_consistent
            Style/HashSyntax:
              EnforcedStyle: hash_rockets
          YAML
      end

      it 'generates Exclude if no EnforcedStyle solves all offenses' do
        create_file('example1.rb', ['# frozen_string_literal: true', '', 'h(:a => 1)', 'h(b: 2)'])

        expect(cli.run(['--auto-gen-config', '--auto-gen-enforced-style'])).to eq(0)
        expect(File.readlines('.rubocop_todo.yml')[10..].join)
          .to eq(<<~YAML)
            # Configuration parameters: EnforcedStyle, EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
            # SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
            # SupportedShorthandSyntax: always, never, either, consistent, either_consistent
            Style/HashSyntax:
              Exclude:
                - 'example1.rb'
          YAML
      end
    end

    describe 'when `--no-auto-gen-enforced-style` is given' do
      it 'generates Exclude if it solves all offenses' do
        create_file('example1.rb', ['# frozen_string_literal: true', '', 'h(:a => 1)'])

        expect(cli.run(['--auto-gen-config', '--no-auto-gen-enforced-style'])).to eq(0)
        expect(File.readlines('.rubocop_todo.yml')[10..].join)
          .to eq(<<~YAML)
            # Configuration parameters: EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
            # SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
            # SupportedShorthandSyntax: always, never, either, consistent, either_consistent
            Style/HashSyntax:
              Exclude:
                - 'example1.rb'
        YAML
      end

      it 'generates Exclude if no EnforcedStyle solves all offenses' do
        create_file('example1.rb', ['# frozen_string_literal: true', '', 'h(:a => 1)', 'h(b: 2)'])

        expect(cli.run(['--auto-gen-config', '--no-auto-gen-enforced-style'])).to eq(0)
        expect(File.readlines('.rubocop_todo.yml')[10..].join)
          .to eq(<<~YAML)
            # Configuration parameters: EnforcedStyle, EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
            # SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
            # SupportedShorthandSyntax: always, never, either, consistent, either_consistent
            Style/HashSyntax:
              Exclude:
                - 'example1.rb'
          YAML
      end

      it 'generates Exclude for expanded EnforcedStyle if it solves all offenses' do
        create_file('example1.rb', ['# frozen_string_literal: true', '', 'h{}'])

        expect(cli.run(['--auto-gen-config', '--no-auto-gen-enforced-style'])).to eq(0)
        expect(File.readlines('.rubocop_todo.yml')[10..].join)
          .to eq(<<~YAML)
            # Configuration parameters: EnforcedStyle.
            # SupportedStyles: space, no_space
            # SupportedStylesForEmptyBraces: space, no_space
            Layout/SpaceBeforeBlockBraces:
              Exclude:
                - 'example1.rb'
          YAML
      end
    end

    context 'when hash value omission enabled', :ruby31 do
      it 'generates Exclude if it solves all offenses' do
        create_file('.rubocop.yml', <<~YAML)
          AllCops:
            NewCops: enable
            TargetRubyVersion: 3.1
          Style/HashSyntax:
            EnforcedShorthandSyntax: always
        YAML
        create_file('example1.rb', ['# frozen_string_literal: true', '', '{ a: a }'])

        expect(cli.run(['--auto-gen-config'])).to eq(0)
        expect(File.readlines('.rubocop_todo.yml')[10..].join)
          .to eq(<<~YAML)
            # Configuration parameters: EnforcedStyle, EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
            # SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
            # SupportedShorthandSyntax: always, never, either, consistent, either_consistent
            Style/HashSyntax:
              Exclude:
                - 'example1.rb'
          YAML
      end
    end

    context 'when there is code conforming to chosen enforced style and code not conforming to any supported style' do
      it 'generates an Exclude list' do
        create_file('.rubocop.yml', <<~YAML)
          AllCops:
            DisabledByDefault: true

          Naming/MethodName:
            Enabled: true
        YAML

        create_file('bad.rb', ['def Bad', 'end'])
        create_file('good.rb', ['def good', 'end'])

        expect(cli.run(['--auto-gen-config'])).to eq(0)
        expect(File.readlines('.rubocop_todo.yml')[10..].join)
          .to eq(<<~YAML)
            # SupportedStyles: snake_case, camelCase
            # ForbiddenIdentifiers: __id__, __send__
            Naming/MethodName:
              Exclude:
                - 'bad.rb'
          YAML
      end
    end

    it 'can be called when there are no files to inspection' do
      expect(cli.run(['--auto-gen-config'])).to eq(0)
    end

    context 'when Max configuration is overridden in .rubocop.yml' do
      it 'generates Exclude instead of Max' do
        create_file('.rubocop.yml', <<~YAML)
          Metrics/ClassLength:
            Max: 250
          Layout/EmptyLinesAroundClassBody:
            Enabled: false
        YAML
        create_file(
          'file.rb',
          [
            '# A long class',
            'class TooLong',
            *Array.new(100) { |i| ["  def method#{i}", "    #{i}", '  end', ''] },
            'end'
          ].flatten
        )
        expect(cli.run(['--auto-gen-config'])).to eq(0)
        expect(File.readlines('.rubocop_todo.yml').grep(/^ *[^#\s]/).join).to eq(<<~YAML)
          Metrics/ClassLength:
            Exclude:
              - 'file.rb'
          Style/FrozenStringLiteralComment:
            Exclude:
              - '**/*.arb'
              - 'file.rb'
        YAML

        # NOTE: Reload CLI to ensure updated config with rubocop_todo is properly picked up.
        #       ConfigStore#for_pwd caches results; re-create CLI to avoid using stale config.
        fresh_cli = RuboCop::CLI.new
        expect(fresh_cli.run([])).to eq(0)
      end
    end
  end
end
