1# Copyright (c) Meta Platforms, Inc. and affiliates.
2#
3# This source code is licensed under the MIT license found in the
4# LICENSE file in the root directory of this source tree.
5
6require "test/unit"
7require_relative "../codegen.rb"
8require_relative "./test_utils/PodMock.rb"
9require_relative "./test_utils/PathnameMock.rb"
10require_relative "./test_utils/FileMock.rb"
11require_relative "./test_utils/DirMock.rb"
12require_relative "./test_utils/systemUtils.rb"
13require_relative "./test_utils/CodegenUtilsMock.rb"
14
15class CodegenTests < Test::Unit::TestCase
16    :third_party_provider_header
17    :third_party_provider_implementation
18    :base_path
19    :prefix
20    :tmp_schema_list_file
21
22    def setup
23        File.enable_testing_mode!
24        Dir.enable_testing_mode!
25        Pod::Config.reset()
26
27        @prefix = "../.."
28        @third_party_provider_header = "RCTThirdPartyFabricComponentsProvider.h"
29        @third_party_provider_implementation = "RCTThirdPartyFabricComponentsProvider.cpp"
30        @base_path = "~/app/ios"
31        @tmp_schema_list_file = "tmpSchemaList.txt"
32        Pathname.pwd!(@base_path)
33        Pod::Config.instance.installation_root.relative_path_from = @base_path
34    end
35
36    def teardown
37        system_reset_commands()
38        Pod::UI.reset()
39        Pod::Executable.reset()
40        Pathname.reset()
41        File.reset()
42        Dir.reset()
43    end
44
45    # ============================================== #
46    # Test - setup_fabric #
47    # ============================================== #
48    def testCheckAndGenerateEmptyThirdPartyProvider_whenFileAlreadyExists_doNothing()
49
50        # Arrange
51        File.mocked_existing_files([
52            @base_path + "/build/" + @third_party_provider_header,
53            @base_path + "/build/" + @third_party_provider_implementation,
54        ])
55
56        # Act
57        checkAndGenerateEmptyThirdPartyProvider!(@prefix, false, 'build')
58
59        # Assert
60        assert_equal(Pathname.pwd_invocation_count, 1)
61        assert_equal(Pod::Config.instance.installation_root.relative_path_from_invocation_count, 1)
62        assert_equal(File.exist_invocation_params, [
63            @base_path + "/build/" + @third_party_provider_header,
64            @base_path + "/build/" + @third_party_provider_implementation,
65        ])
66        assert_equal(Dir.exist_invocation_params, [])
67        assert_equal(Pod::UI.collected_messages, [])
68        assert_equal($collected_commands, [])
69        assert_equal(File.open_files.length, 0)
70        assert_equal(Pod::Executable.executed_commands.length, 0)
71    end
72
73    def testCheckAndGenerateEmptyThirdPartyProvider_whenHeaderMissingAndCodegenMissing_raiseError()
74
75        # Arrange
76        File.mocked_existing_files([
77            @base_path + "/build/" + @third_party_provider_implementation,
78        ])
79
80        # Act
81        assert_raise {
82            checkAndGenerateEmptyThirdPartyProvider!(@prefix, false, 'build')
83        }
84
85        # Assert
86        assert_equal(Pathname.pwd_invocation_count, 1)
87        assert_equal(Pod::Config.instance.installation_root.relative_path_from_invocation_count, 1)
88        assert_equal(File.exist_invocation_params, [
89            @base_path + "/build/" + @third_party_provider_header
90        ])
91        assert_equal(Dir.exist_invocation_params, [
92            @base_path + "/"+ @prefix + "/packages/react-native-codegen",
93            @base_path + "/"+ @prefix + "/../react-native-codegen",
94        ])
95        assert_equal(Pod::UI.collected_messages, [])
96        assert_equal($collected_commands, [])
97        assert_equal(File.open_files.length, 0)
98        assert_equal(Pod::Executable.executed_commands.length, 0)
99    end
100
101    def testCheckAndGenerateEmptyThirdPartyProvider_whenImplementationMissingAndCodegenrepoExists_dontBuildCodegen()
102
103        # Arrange
104        File.mocked_existing_files([
105            @base_path + "/build/" + @third_party_provider_header,
106            @base_path + "/build/tmpSchemaList.txt"
107        ])
108
109        Dir.mocked_existing_dirs([
110            @base_path + "/"+ @prefix + "/packages/react-native-codegen",
111            @base_path + "/"+ @prefix + "/packages/react-native-codegen/lib"
112        ])
113
114        # Act
115        checkAndGenerateEmptyThirdPartyProvider!(@prefix, false, 'build')
116
117        # Assert
118        assert_equal(Pathname.pwd_invocation_count, 1)
119        assert_equal(Pod::Config.instance.installation_root.relative_path_from_invocation_count, 1)
120        assert_equal(File.exist_invocation_params, [
121            @base_path + "/build/" + @third_party_provider_header,
122            @base_path + "/build/" + @third_party_provider_implementation,
123            @base_path + "/build/tmpSchemaList.txt",
124        ])
125        assert_equal(Dir.exist_invocation_params, [
126            @base_path + "/"+ @prefix + "/packages/react-native-codegen",
127            @base_path + "/"+ @prefix + "/packages/react-native-codegen/lib",
128        ])
129        assert_equal(Pod::UI.collected_messages, ["[Codegen] generating an empty RCTThirdPartyFabricComponentsProvider"])
130        assert_equal($collected_commands, [])
131        assert_equal(File.open_invocation_count, 1)
132        assert_equal(File.open_files_with_mode[@base_path + "/build/tmpSchemaList.txt"], 'w')
133        assert_equal(File.open_files[0].collected_write, ["[]"])
134        assert_equal(File.open_files[0].fsync_invocation_count, 1)
135        assert_equal(Pod::Executable.executed_commands[0], {
136            "command" => "node",
137            "arguments" => [
138                @base_path + "/" + @prefix + "/scripts/generate-provider-cli.js",
139                "--platform", 'ios',
140                "--schemaListPath", @base_path + "/build/tmpSchemaList.txt",
141                "--outputDir", @base_path + "/build"
142            ]
143        })
144        assert_equal(File.delete_invocation_count, 1)
145        assert_equal(File.deleted_files, [@base_path + "/build/tmpSchemaList.txt"])
146    end
147
148    def testCheckAndGenerateEmptyThirdPartyProvider_whenBothMissing_buildCodegen()
149        # Arrange
150        codegen_cli_path = @base_path + "/" + @prefix + "/../react-native-codegen"
151        Dir.mocked_existing_dirs([
152            codegen_cli_path,
153        ])
154        # Act
155        checkAndGenerateEmptyThirdPartyProvider!(@prefix, false, 'build')
156
157        # Assert
158        assert_equal(Pathname.pwd_invocation_count, 1)
159        assert_equal(Pod::Config.instance.installation_root.relative_path_from_invocation_count, 1)
160        assert_equal(File.exist_invocation_params, [
161            @base_path + "/build/" + @third_party_provider_header,
162            @base_path + "/build/" + @tmp_schema_list_file
163        ])
164        assert_equal(Dir.exist_invocation_params, [
165            @base_path + "/" + @prefix + "/packages/react-native-codegen",
166            codegen_cli_path,
167            codegen_cli_path + "/lib",
168        ])
169        assert_equal(Pod::UI.collected_messages, [
170            "[Codegen] building #{codegen_cli_path}.",
171            "[Codegen] generating an empty RCTThirdPartyFabricComponentsProvider"
172        ])
173        assert_equal($collected_commands, ["~/app/ios/../../../react-native-codegen/scripts/oss/build.sh"])
174        assert_equal(File.open_files[0].collected_write, ["[]"])
175        assert_equal(File.open_files[0].fsync_invocation_count, 1)
176        assert_equal(Pod::Executable.executed_commands[0], {
177            "command" => "node",
178            "arguments" => [
179                @base_path + "/" + @prefix + "/scripts/generate-provider-cli.js",
180                "--platform", 'ios',
181                "--schemaListPath", @base_path + "/build/" + @tmp_schema_list_file,
182                "--outputDir", @base_path + "/build"
183            ]
184        })
185    end
186
187    # ================= #
188    # Test - RunCodegen #
189    # ================= #
190    def testRunCodegen_whenNewArchEnabled_runsCodegen
191        # Arrange
192        app_path = "~/app"
193        config_file = ""
194        codegen_utils_mock = CodegenUtilsMock.new()
195
196        # Act
197        run_codegen!(app_path, config_file, :new_arch_enabled => true, :codegen_utils => codegen_utils_mock)
198
199        # Assert
200        assert_equal(codegen_utils_mock.use_react_native_codegen_discovery_params, [{
201            :app_path=>"~/app",
202            :codegen_disabled=>false,
203            :codegen_output_dir=>"build/generated/ios",
204            :config_file_dir=>"",
205            :fabric_enabled=>false,
206            :folly_version=>"2021.07.22.00",
207            :react_native_path=>"../node_modules/react-native"
208        }])
209        assert_equal(codegen_utils_mock.get_react_codegen_spec_params, [])
210        assert_equal(codegen_utils_mock.generate_react_codegen_spec_params, [])
211    end
212
213    def testRunCodegen_whenNewArchDisabled_runsCodegen
214        # Arrange
215        app_path = "~/app"
216        config_file = ""
217        package_json_file = "~/app/package.json"
218        codegen_specs = { "name" => "ABI48_0_0React-Codegen" }
219        codegen_utils_mock = CodegenUtilsMock.new(:react_codegen_spec => codegen_specs)
220
221        # Act
222        run_codegen!(
223            app_path,
224            config_file,
225            :new_arch_enabled => false,
226            :fabric_enabled => true,
227            :package_json_file => package_json_file,
228            :codegen_utils => codegen_utils_mock)
229
230        # Assert
231        assert_equal(codegen_utils_mock.use_react_native_codegen_discovery_params, [])
232        assert_equal(codegen_utils_mock.get_react_codegen_spec_params, [{
233            :fabric_enabled => true,
234            :folly_version=>"2021.07.22.00",
235            :package_json_file => "~/app/package.json",
236            :script_phases => nil
237        }])
238        assert_equal(codegen_utils_mock.generate_react_codegen_spec_params, [{
239            :codegen_output_dir=>"build/generated/ios",
240            :react_codegen_spec=>{"name"=>"ABI48_0_0React-Codegen"}
241        }])
242
243    end
244end
245