def test_main_doesnt_run_if_not_main(self):
        with making_combined_info([(2020, 3, 'a')], None) as combined_info:
            challenge = combined_info.get_challenge_instance(2020, 3, 'a')

            with mock.patch.object(challenge, 'solve') as mocked:
                challenge.main([])
                self.assertEqual(mocked.call_count, 0)
 def test_input(self):
     with making_combined_info([(2020, 3, 'a')], None) as combined_info:
         part = combined_info.get_part(2020, 3, 'a')
         part.get_input_filename()\
             .write_text("Custom Input\nOver Multiple\nLines")
         challenge = combined_info.get_challenge_instance(2020, 3, 'a')
         self.assertEqual(challenge.input,
                          "Custom Input\nOver Multiple\nLines")
 def test_generate_with_one_year_with_some_stars_and_code(self):
     with making_combined_info(
         [
             (2020, 1, 'a'),
             (2020, 1, 'b'),
             (2020, 2, 'a'),
             (2020, 2, 'b'),
             (2020, 3, 'a'),
         ],
         {
             "username": "******",
             "total_stars": 3,
             "years": {
                 2020: {
                     "year": 2020,
                     "stars": 3,
                     "days": {
                         1: 2,
                         2: 1,
                         3: 0,
                     }
                 },
             },
         },
     ) as combined_info:
         self.assertEqual(SubmissionsSummary().generate(combined_info), (
             "\n\n"
             "|       | 2020                                                             |\n"
             "|  ---: | :---:                                                            |\n"
             "|       | [Code][co-20]    &                         [Challenges][ch-20]   |\n"
             "|       | 3 :star: / 2 :x: / 1 :grey_exclamation:                          |\n"
             "|  1    | [Code][co-20-01] :star: :star:             [Challenge][ch-20-01] |\n"
             "|  2    | [Code][co-20-02] :star: :x:                [Challenge][ch-20-02] |\n"
             "|  3    | [Code][co-20-03] :x:    :grey_exclamation: [Challenge][ch-20-03] |\n"
             "|  4    | Code                                       [Challenge][ch-20-04] |\n"
             "|  5    | Code                                       [Challenge][ch-20-05] |\n"
             "|  6    | Code                                       [Challenge][ch-20-06] |\n"
             "|  7    | Code                                       [Challenge][ch-20-07] |\n"
             "|  8    | Code                                       [Challenge][ch-20-08] |\n"
             "|  9    | Code                                       [Challenge][ch-20-09] |\n"
             "| 10    | Code                                       [Challenge][ch-20-10] |\n"
             "| 11    | Code                                       [Challenge][ch-20-11] |\n"
             "| 12    | Code                                       [Challenge][ch-20-12] |\n"
             "| 13    | Code                                       [Challenge][ch-20-13] |\n"
             "| 14    | Code                                       [Challenge][ch-20-14] |\n"
             "| 15    | Code                                       [Challenge][ch-20-15] |\n"
             "| 16    | Code                                       [Challenge][ch-20-16] |\n"
             "| 17    | Code                                       [Challenge][ch-20-17] |\n"
             "| 18    | Code                                       [Challenge][ch-20-18] |\n"
             "| 19    | Code                                       [Challenge][ch-20-19] |\n"
             "| 20    | Code                                       [Challenge][ch-20-20] |\n"
             "| 21    | Code                                       [Challenge][ch-20-21] |\n"
             "| 22    | Code                                       [Challenge][ch-20-22] |\n"
             "| 23    | Code                                       [Challenge][ch-20-23] |\n"
             "| 24    | Code                                       [Challenge][ch-20-24] |\n"
             "| 25    | Code                                       [Challenge][ch-20-25] |\n"
             + self.get_years_links([2020]) + "\n"))
 def test_input_is_given_through_default_solve(self):
     with making_combined_info([(2020, 3, 'a')], None) as combined_info:
         part = combined_info.get_part(2020, 3, 'a')
         part.get_input_filename()\
             .write_text("Custom Input\nOver Multiple\nLines")
         challenge = combined_info.get_challenge_instance(2020, 3, 'a')
         with mock.patch.object(challenge, 'solve') as mocked:
             challenge.default_solve()
             self.assertEqual(mocked.call_count, 1)
             self.assertEqual(
                 mocked.call_args,
                 mock.call("Custom Input\nOver Multiple\nLines", debug=ANY))
 def test_generate_with_one_year_with_all_stars_and_code(self):
     with making_combined_info(
         [(2020, day, part) for part in ['a', 'b'] for day in range(1, 26)],
         {
             "username": "******",
             "total_stars": 50,
             "years": {
                 2020: {
                     "year": 2020,
                     "stars": 50,
                     "days": {day: 2
                              for day in range(1, 26)}
                 },
             },
         },
     ) as combined_info:
         self.assertEqual(SubmissionsSummary().generate(combined_info), (
             "\n\n"
             "|       | 2020                                                 |\n"
             "|  ---: | :---:                                                |\n"
             "|       | [Code][co-20]    &             [Challenges][ch-20]   |\n"
             "|       | 50 :star: :star:                                     |\n"
             "|  1    | [Code][co-20-01] :star: :star: [Challenge][ch-20-01] |\n"
             "|  2    | [Code][co-20-02] :star: :star: [Challenge][ch-20-02] |\n"
             "|  3    | [Code][co-20-03] :star: :star: [Challenge][ch-20-03] |\n"
             "|  4    | [Code][co-20-04] :star: :star: [Challenge][ch-20-04] |\n"
             "|  5    | [Code][co-20-05] :star: :star: [Challenge][ch-20-05] |\n"
             "|  6    | [Code][co-20-06] :star: :star: [Challenge][ch-20-06] |\n"
             "|  7    | [Code][co-20-07] :star: :star: [Challenge][ch-20-07] |\n"
             "|  8    | [Code][co-20-08] :star: :star: [Challenge][ch-20-08] |\n"
             "|  9    | [Code][co-20-09] :star: :star: [Challenge][ch-20-09] |\n"
             "| 10    | [Code][co-20-10] :star: :star: [Challenge][ch-20-10] |\n"
             "| 11    | [Code][co-20-11] :star: :star: [Challenge][ch-20-11] |\n"
             "| 12    | [Code][co-20-12] :star: :star: [Challenge][ch-20-12] |\n"
             "| 13    | [Code][co-20-13] :star: :star: [Challenge][ch-20-13] |\n"
             "| 14    | [Code][co-20-14] :star: :star: [Challenge][ch-20-14] |\n"
             "| 15    | [Code][co-20-15] :star: :star: [Challenge][ch-20-15] |\n"
             "| 16    | [Code][co-20-16] :star: :star: [Challenge][ch-20-16] |\n"
             "| 17    | [Code][co-20-17] :star: :star: [Challenge][ch-20-17] |\n"
             "| 18    | [Code][co-20-18] :star: :star: [Challenge][ch-20-18] |\n"
             "| 19    | [Code][co-20-19] :star: :star: [Challenge][ch-20-19] |\n"
             "| 20    | [Code][co-20-20] :star: :star: [Challenge][ch-20-20] |\n"
             "| 21    | [Code][co-20-21] :star: :star: [Challenge][ch-20-21] |\n"
             "| 22    | [Code][co-20-22] :star: :star: [Challenge][ch-20-22] |\n"
             "| 23    | [Code][co-20-23] :star: :star: [Challenge][ch-20-23] |\n"
             "| 24    | [Code][co-20-24] :star: :star: [Challenge][ch-20-24] |\n"
             "| 25    | [Code][co-20-25] :star: :star: [Challenge][ch-20-25] |\n"
             + self.get_years_links([2020]) + "\n"))
    def test_main_runs_if_main(self):
        with making_combined_info([(2020, 3, 'a')], None) as combined_info:
            part = combined_info.get_part(2020, 3, 'a')
            part.get_input_filename()\
                .write_text("Custom Input\nOver Multiple\nLines")
            challenge = combined_info.get_challenge_instance(2020, 3, 'a')

            with mock.patch.object(challenge, 'solve') as solve_mocked:
                with mock.patch.object(type(challenge), 'is_main_module') \
                        as is_main_module_mocked:
                    is_main_module_mocked.return_value = True
                    with self.assertRaises(SystemExit):
                        challenge.main([])
                    self.assertEqual(solve_mocked.call_count, 1)
                    self.assertEqual(
                        solve_mocked.call_args,
                        mock.call("Custom Input\nOver Multiple\nLines",
                                  debug=ANY))
 def test_parsed_year_day_and_part(self):
     with making_combined_info([(2020, 3, 'a')], None) as combined_info:
         challenge = combined_info.get_challenge_instance(2020, 3, 'a')
         self.assertIsNotNone(challenge)
         self.assertEqual((challenge.year, challenge.day, challenge.part),
                          (2020, 3, 'a'))
    def test_repo_data(self):
        with making_combined_info(
                self.parts_to_create_on_disk, self.collected_data) \
                as combined_info:
            folder_contents = glob.glob(
                f"{settings_proxy().challenges_root}/**/*", recursive=True)

            self.assertEqual(combined_info.has_code, self.has_code)
            self.assertTrue(set(combined_info.year_infos).issuperset(
                {2015, 2016, 2017, 2018, 2019, 2020}))

            # Check years with code
            self.assertEqual({
                year_info.year
                for year_info in combined_info.year_infos.values()
                if str(year_info.path) in folder_contents
            }, self.years_with_code)
            self.assertEqual({
                year_info.year
                for year_info in combined_info.year_infos.values()
                if year_info.has_code
            }, self.years_with_code)
            self.assertEqual({
                year_info.year
                for year_info in combined_info.year_infos.values()
                if year_info.path.exists()
            }, self.years_with_code)

            # Check days with code
            self.assertEqual({
                (day_info.year, day_info.day)
                for year_info in combined_info.year_infos.values()
                for day_info in year_info.day_infos.values()
                if str(day_info.path) in folder_contents
            }, self.days_with_code)
            self.assertEqual({
                (day_info.year, day_info.day)
                for year_info in combined_info.year_infos.values()
                for day_info in year_info.day_infos.values()
                if day_info.has_code
            }, self.days_with_code)
            self.assertEqual({
                (day_info.year, day_info.day)
                for year_info in combined_info.year_infos.values()
                for day_info in year_info.day_infos.values()
                if day_info.path.exists()
            }, self.days_with_code)

            # Check parts with code
            self.assertEqual({
                (part_info.year, part_info.day, part_info.part)
                for year_info in combined_info.year_infos.values()
                for day_info in year_info.day_infos.values()
                for part_info in day_info.part_infos.values()
                if str(part_info.path) in folder_contents
            }, self.parts_with_code)
            self.assertEqual({
                (part_info.year, part_info.day, part_info.part)
                for year_info in combined_info.year_infos.values()
                for day_info in year_info.day_infos.values()
                for part_info in day_info.part_infos.values()
                if part_info.has_code
            }, self.parts_with_code)
            self.assertEqual({
                (part_info.year, part_info.day, part_info.part)
                for year_info in combined_info.year_infos.values()
                for day_info in year_info.day_infos.values()
                for part_info in day_info.part_infos.values()
                if part_info.path.exists()
            }, self.parts_with_code)

            for year, year_info in combined_info.year_infos.items():
                self.assertEqual(year_info.year, year)
                self.assertEqual(
                    set(year_info.day_infos), set(range(1, 26)))
                for day, day_info in year_info.day_infos.items():
                    self.assertEqual(day_info.year_info, year_info)
                    self.assertEqual(day_info.year, year)
                    self.assertEqual(day_info.day, day)
                    self.assertEqual(set(day_info.part_infos), {'a', 'b'})
                    for part, part_info in day_info.part_infos.items():
                        self.assertEqual(part_info.day_info, day_info)
                        self.assertEqual(part_info.year, year)
                        self.assertEqual(part_info.day, day)
                        self.assertEqual(part_info.part, part)
 def test_generate_with_multiple_years_with_different_stars_and_code(self):
     with making_combined_info(
         [(2020, day, part) for part in ['a', 'b']
          for day in range(1, 26)] + [
              (2018, 1, 'a'),
              (2018, 1, 'b'),
              (2018, 2, 'a'),
              (2018, 2, 'b'),
              (2018, 3, 'a'),
          ],
         {
             "username": "******",
             "total_stars": 53,
             "years": {
                 2020: {
                     "year": 2020,
                     "stars": 50,
                     "days": {day: 2
                              for day in range(1, 26)}
                 },
                 2018: {
                     "year": 2018,
                     "stars": 3,
                     "days": {
                         1: 2,
                         2: 1,
                         3: 0,
                     }
                 },
             },
         },
     ) as combined_info:
         self.assertEqual(SubmissionsSummary().generate(combined_info), (
             "\n\n"
             "|       | 2020                                                 | 2018                                                             |\n"
             "|  ---: | :---:                                                | :---:                                                            |\n"
             "|       | [Code][co-20]    &             [Challenges][ch-20]   | [Code][co-18]    &                         [Challenges][ch-18]   |\n"
             "|       | 50 :star: :star:                                     | 3 :star: / 2 :x: / 1 :grey_exclamation:                          |\n"
             "|  1    | [Code][co-20-01] :star: :star: [Challenge][ch-20-01] | [Code][co-18-01] :star: :star:             [Challenge][ch-18-01] |\n"
             "|  2    | [Code][co-20-02] :star: :star: [Challenge][ch-20-02] | [Code][co-18-02] :star: :x:                [Challenge][ch-18-02] |\n"
             "|  3    | [Code][co-20-03] :star: :star: [Challenge][ch-20-03] | [Code][co-18-03] :x:    :grey_exclamation: [Challenge][ch-18-03] |\n"
             "|  4    | [Code][co-20-04] :star: :star: [Challenge][ch-20-04] | Code                                       [Challenge][ch-18-04] |\n"
             "|  5    | [Code][co-20-05] :star: :star: [Challenge][ch-20-05] | Code                                       [Challenge][ch-18-05] |\n"
             "|  6    | [Code][co-20-06] :star: :star: [Challenge][ch-20-06] | Code                                       [Challenge][ch-18-06] |\n"
             "|  7    | [Code][co-20-07] :star: :star: [Challenge][ch-20-07] | Code                                       [Challenge][ch-18-07] |\n"
             "|  8    | [Code][co-20-08] :star: :star: [Challenge][ch-20-08] | Code                                       [Challenge][ch-18-08] |\n"
             "|  9    | [Code][co-20-09] :star: :star: [Challenge][ch-20-09] | Code                                       [Challenge][ch-18-09] |\n"
             "| 10    | [Code][co-20-10] :star: :star: [Challenge][ch-20-10] | Code                                       [Challenge][ch-18-10] |\n"
             "| 11    | [Code][co-20-11] :star: :star: [Challenge][ch-20-11] | Code                                       [Challenge][ch-18-11] |\n"
             "| 12    | [Code][co-20-12] :star: :star: [Challenge][ch-20-12] | Code                                       [Challenge][ch-18-12] |\n"
             "| 13    | [Code][co-20-13] :star: :star: [Challenge][ch-20-13] | Code                                       [Challenge][ch-18-13] |\n"
             "| 14    | [Code][co-20-14] :star: :star: [Challenge][ch-20-14] | Code                                       [Challenge][ch-18-14] |\n"
             "| 15    | [Code][co-20-15] :star: :star: [Challenge][ch-20-15] | Code                                       [Challenge][ch-18-15] |\n"
             "| 16    | [Code][co-20-16] :star: :star: [Challenge][ch-20-16] | Code                                       [Challenge][ch-18-16] |\n"
             "| 17    | [Code][co-20-17] :star: :star: [Challenge][ch-20-17] | Code                                       [Challenge][ch-18-17] |\n"
             "| 18    | [Code][co-20-18] :star: :star: [Challenge][ch-20-18] | Code                                       [Challenge][ch-18-18] |\n"
             "| 19    | [Code][co-20-19] :star: :star: [Challenge][ch-20-19] | Code                                       [Challenge][ch-18-19] |\n"
             "| 20    | [Code][co-20-20] :star: :star: [Challenge][ch-20-20] | Code                                       [Challenge][ch-18-20] |\n"
             "| 21    | [Code][co-20-21] :star: :star: [Challenge][ch-20-21] | Code                                       [Challenge][ch-18-21] |\n"
             "| 22    | [Code][co-20-22] :star: :star: [Challenge][ch-20-22] | Code                                       [Challenge][ch-18-22] |\n"
             "| 23    | [Code][co-20-23] :star: :star: [Challenge][ch-20-23] | Code                                       [Challenge][ch-18-23] |\n"
             "| 24    | [Code][co-20-24] :star: :star: [Challenge][ch-20-24] | Code                                       [Challenge][ch-18-24] |\n"
             "| 25    | [Code][co-20-25] :star: :star: [Challenge][ch-20-25] | Code                                       [Challenge][ch-18-25] |\n"
             + self.get_years_links([2020, 2018]) + "\n"))