def test_component() -> None:
    component = Component(
        '00c36da8-e22b-43a1-9a87-c3a67e863f49', Name('Generic Connector 1x27'),
        Description(r'A 1x27 soldered wire connector.\n\nNext line'),
        Keywords('connector, 1x27'), Author('Test R.'), Version('0.2'),
        Created('2018-10-17T19:13:41Z'), Deprecated(False),
        Category('d0618c29-0436-42da-a388-fdadf7b23892'), SchematicOnly(False),
        DefaultValue(''), Prefix('J'))
    component.add_signal(
        Signal('f46a4643-fc68-4593-a889-3d987bfe3544', Name('1'), Role.PASSIVE,
               Required(False), Negated(False), Clock(False), ForcedNet('')))

    gate = Gate('c1e4b542-a1b1-44d5-bec3-070776143a29',
                SymbolUUID('8f1a97f2-4cdf-43da-b38d-b3787c47b5ad'),
                Position(0.0, 0.0), Rotation(0.0), Required(True), Suffix(''))
    gate.add_pin_signal_map(
        PinSignalMap('0189aafc-f88a-4e65-8fb4-09a047a3e334',
                     SignalUUID('46f7e0e2-74a6-442b-9a5c-1bd4ea3da59c'),
                     TextDesignator.SYMBOL_PIN_NAME))
    variant = Variant('abeeeed0-6e9a-4fdc-bc2b-e2c5b06bbe3a', Norm.EMPTY,
                      Name('default'), Description(''), gate)

    component.add_variant(variant)

    assert str(component
               ) == """(librepcb_component 00c36da8-e22b-43a1-9a87-c3a67e863f49
def test_component_gate() -> None:
    gate = Gate('c1e4b542-a1b1-44d5-bec3-070776143a29',
                SymbolUUID('8f1a97f2-4cdf-43da-b38d-b3787c47b5ad'),
                Position(0.0, 0.0), Rotation(0.0), Required(True), Suffix(''))
    gate.add_pin_signal_map(
        PinSignalMap('0189aafc-f88a-4e65-8fb4-09a047a3e334',
                     SignalUUID('46f7e0e2-74a6-442b-9a5c-1bd4ea3da59c'),
                     TextDesignator.SYMBOL_PIN_NAME))
    assert str(gate) == """(gate c1e4b542-a1b1-44d5-bec3-070776143a29
def test_component_variant() -> None:
    gate = Gate('c1e4b542-a1b1-44d5-bec3-070776143a29',
                SymbolUUID('8f1a97f2-4cdf-43da-b38d-b3787c47b5ad'),
                Position(0.0, 0.0), Rotation(0.0), Required(True), Suffix(''))
    gate.add_pin_signal_map(
        PinSignalMap('0189aafc-f88a-4e65-8fb4-09a047a3e334',
                     SignalUUID('46f7e0e2-74a6-442b-9a5c-1bd4ea3da59c'),
                     TextDesignator.SYMBOL_PIN_NAME))
    variant = Variant('abeeeed0-6e9a-4fdc-bc2b-e2c5b06bbe3a', Norm.EMPTY,
                      Name('default'), Description(''), gate)
    assert str(
        variant) == """(variant abeeeed0-6e9a-4fdc-bc2b-e2c5b06bbe3a (norm "")
def generate_cmp(
    name: str,
    mcus: List[MCU],
    symbol_map: Dict[str, str],
    debug: bool = False,
) -> None:
    """
    When generating components, to reduce the number of components, they are
    merged as follows:

    - For every MCU, the "ref without flash" is calculated by replacing the
      11th character in the ref name with an `x` and cutting off everything
      after the package character.
    - MCUs that share the same pinout will be merged if their ref names without
      flash are the same
    - The name of the component will be generated as follows: STM32F429IEHx +
      STM32F429IGHx + STM32F429IIHx = STM32F429I[EGI]Hx.
    - To achieve a stable UUID even if new MCUs are added, the "ref without
      flash" is combined with the SHA1 hash of the pins.

    Because renaming the pinout might result in a different UUID, when
    upgrading the stm-pinout database, changes (but not additions) must be
    analyzed manually.

    """
    components = []
    for mcu in mcus:
        (placement, pin_mapping) = mcu.generate_placement_data(debug)

        cmp_version = '0.1'

        component = Component(
            uuid('cmp', mcu.component_identifier, 'cmp'),
            Name(name),
            Description(mcu.component_description),
            mcu.keywords,
            author,
            Version(cmp_version),
            Created('2020-01-30T20:55:23Z'),
            Deprecated(False),
            cmpcat,
            SchematicOnly(False),
            DefaultValue('{{PARTNUMBER or DEVICE or COMPONENT}}'),
            Prefix('U'),
        )

        # Add signals
        signals = sorted({pin.name for pin in mcu.pins}, key=human_sort_key)
        for signal in signals:
            component.add_signal(Signal(
                # Use original signal name, so that changing the cleanup function
                # does not influence the identifier.
                uuid('cmp', mcu.component_identifier, 'signal-{}'.format(signal)),
                # Use cleaned up signal name for name
                Name(signal),
                Role.PASSIVE,
                Required(False),
                Negated(False),
                Clock(False),
                ForcedNet(''),
            ))

        # Add symbol variant
        gate = Gate(
            uuid('cmp', mcu.component_identifier, 'variant-single-gate1'),
            SymbolUUID(uuid('sym', mcu.symbol_identifier, 'sym')),
            Position(0, 0),
            Rotation(0.0),
            Required(True),
            Suffix(''),
        )
        for generic, concrete in pin_mapping.items():
            gate.add_pin_signal_map(PinSignalMap(
                uuid('sym', mcu.symbol_identifier, 'pin-{}'.format(generic)),
                SignalUUID(uuid('cmp', mcu.component_identifier, 'signal-{}'.format(concrete))),
                TextDesignator.SIGNAL_NAME,
            ))
        component.add_variant(Variant(
            uuid('cmp', mcu.component_identifier, 'variant-single'),
            Norm.EMPTY,
            Name('single'),
            Description('Symbol with all MCU pins'),
            gate,
        ))
        components.append(component)

    # Make sure all grouped components are identical
    assert len(set([str(c) for c in components])) == 1

    components[0].serialize('out/stm_mcu/cmp')

    print('Wrote cmp {}'.format(name))
def generate_cmp(
    dirpath: str,
    author: str,
    name: str,
    name_lower: str,
    kind: str,
    cmpcat: str,
    keywords: str,
    default_value: str,
    rows: int,
    min_pads: int,
    max_pads: int,
    version: str,
    create_date: Optional[str],
) -> None:
    category = 'cmp'
    assert rows in [1, 2]
    for i in range(min_pads, max_pads + 1, rows):
        per_row = i // rows
        variant = '{}x{}'.format(rows, per_row)

        def _uuid(identifier: str) -> str:
            return uuid(category, kind, variant, identifier)

        uuid_cmp = _uuid('cmp')
        uuid_pins = [
            uuid('sym', kind, variant, 'pin-{}'.format(p)) for p in range(i)
        ]
        uuid_signals = [_uuid('signal-{}'.format(p)) for p in range(i)]
        uuid_variant = _uuid('variant-default')
        uuid_gate = _uuid('gate-default')
        uuid_symbol = uuid('sym', kind, variant, 'sym')

        # General info
        component = Component(
            uuid_cmp,
            Name('{} {}x{:02d}'.format(name, rows, per_row)),
            Description('A {}x{} {}.\\n\\n'
                        'Generated with {}'.format(rows, per_row, name_lower,
                                                   generator)),
            Keywords('connector, {}x{}, {}'.format(rows, per_row, keywords)),
            Author(author),
            Version(version),
            Created(create_date or now()),
            Deprecated(False),
            Category(cmpcat),
            SchematicOnly(False),
            DefaultValue(default_value),
            Prefix('J'),
        )

        for p in range(1, i + 1):
            component.add_signal(
                Signal(
                    uuid_signals[p - 1],
                    Name(str(p)),
                    Role.PASSIVE,
                    Required(False),
                    Negated(False),
                    Clock(False),
                    ForcedNet(''),
                ))

        gate = Gate(
            uuid_gate,
            SymbolUUID(uuid_symbol),
            Position(0.0, 0.0),
            Rotation(0.0),
            Required(True),
            Suffix(''),
        )
        for p in range(1, i + 1):
            gate.add_pin_signal_map(
                PinSignalMap(
                    uuid_pins[p - 1],
                    SignalUUID(uuid_signals[p - 1]),
                    TextDesignator.SYMBOL_PIN_NAME,
                ))

        component.add_variant(
            Variant(uuid_variant, Norm.EMPTY, Name('default'), Description(''),
                    gate))

        component.serialize(dirpath)

        print('{}x{} {}: Wrote component {}'.format(rows, per_row, kind,
                                                    uuid_cmp))